Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.server.pm;
     17 
     18 import android.annotation.NonNull;
     19 import android.annotation.UserIdInt;
     20 import android.content.ComponentName;
     21 import android.text.format.Formatter;
     22 import android.util.ArrayMap;
     23 import android.util.Slog;
     24 import android.util.SparseArray;
     25 
     26 import com.android.internal.annotations.VisibleForTesting;
     27 import com.android.internal.util.Preconditions;
     28 
     29 import libcore.util.Objects;
     30 
     31 import org.xmlpull.v1.XmlPullParser;
     32 import org.xmlpull.v1.XmlPullParserException;
     33 import org.xmlpull.v1.XmlSerializer;
     34 
     35 import java.io.File;
     36 import java.io.IOException;
     37 import java.io.PrintWriter;
     38 import java.util.function.Consumer;
     39 
     40 /**
     41  * User information used by {@link ShortcutService}.
     42  */
     43 class ShortcutUser {
     44     private static final String TAG = ShortcutService.TAG;
     45 
     46     static final String TAG_ROOT = "user";
     47     private static final String TAG_LAUNCHER = "launcher";
     48 
     49     private static final String ATTR_VALUE = "value";
     50     private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no";
     51 
     52     static final class PackageWithUser {
     53         final int userId;
     54         final String packageName;
     55 
     56         private PackageWithUser(int userId, String packageName) {
     57             this.userId = userId;
     58             this.packageName = Preconditions.checkNotNull(packageName);
     59         }
     60 
     61         public static PackageWithUser of(int userId, String packageName) {
     62             return new PackageWithUser(userId, packageName);
     63         }
     64 
     65         public static PackageWithUser of(ShortcutPackageItem spi) {
     66             return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName());
     67         }
     68 
     69         @Override
     70         public int hashCode() {
     71             return packageName.hashCode() ^ userId;
     72         }
     73 
     74         @Override
     75         public boolean equals(Object obj) {
     76             if (!(obj instanceof PackageWithUser)) {
     77                 return false;
     78             }
     79             final PackageWithUser that = (PackageWithUser) obj;
     80 
     81             return userId == that.userId && packageName.equals(that.packageName);
     82         }
     83 
     84         @Override
     85         public String toString() {
     86             return String.format("{Package: %d, %s}", userId, packageName);
     87         }
     88     }
     89 
     90     @UserIdInt
     91     private final int mUserId;
     92 
     93     private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
     94 
     95     private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>();
     96 
     97     private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
     98 
     99     /** Default launcher that can access the launcher apps APIs. */
    100     private ComponentName mLauncherComponent;
    101 
    102     private long mKnownLocaleChangeSequenceNumber;
    103 
    104     public ShortcutUser(int userId) {
    105         mUserId = userId;
    106     }
    107 
    108     public int getUserId() {
    109         return mUserId;
    110     }
    111 
    112     // We don't expose this directly to non-test code because only ShortcutUser should add to/
    113     // remove from it.
    114     @VisibleForTesting
    115     ArrayMap<String, ShortcutPackage> getAllPackagesForTest() {
    116         return mPackages;
    117     }
    118 
    119     public ShortcutPackage removePackage(@NonNull ShortcutService s, @NonNull String packageName) {
    120         final ShortcutPackage removed = mPackages.remove(packageName);
    121 
    122         s.cleanupBitmapsForPackage(mUserId, packageName);
    123 
    124         return removed;
    125     }
    126 
    127     // We don't expose this directly to non-test code because only ShortcutUser should add to/
    128     // remove from it.
    129     @VisibleForTesting
    130     ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchersForTest() {
    131         return mLaunchers;
    132     }
    133 
    134     public void addLauncher(ShortcutLauncher launcher) {
    135         mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(),
    136                 launcher.getPackageName()), launcher);
    137     }
    138 
    139     public ShortcutLauncher removeLauncher(
    140             @UserIdInt int packageUserId, @NonNull String packageName) {
    141         return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
    142     }
    143 
    144     public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) {
    145         ShortcutPackage ret = mPackages.get(packageName);
    146         if (ret == null) {
    147             ret = new ShortcutPackage(s, this, mUserId, packageName);
    148             mPackages.put(packageName, ret);
    149         } else {
    150             ret.attemptToRestoreIfNeededAndSave(s);
    151         }
    152         return ret;
    153     }
    154 
    155     public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName,
    156             @UserIdInt int launcherUserId) {
    157         final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
    158         ShortcutLauncher ret = mLaunchers.get(key);
    159         if (ret == null) {
    160             ret = new ShortcutLauncher(this, mUserId, packageName, launcherUserId);
    161             mLaunchers.put(key, ret);
    162         } else {
    163             ret.attemptToRestoreIfNeededAndSave(s);
    164         }
    165         return ret;
    166     }
    167 
    168     public void forAllPackages(Consumer<? super ShortcutPackage> callback) {
    169         final int size = mPackages.size();
    170         for (int i = 0; i < size; i++) {
    171             callback.accept(mPackages.valueAt(i));
    172         }
    173     }
    174 
    175     public void forAllLaunchers(Consumer<? super ShortcutLauncher> callback) {
    176         final int size = mLaunchers.size();
    177         for (int i = 0; i < size; i++) {
    178             callback.accept(mLaunchers.valueAt(i));
    179         }
    180     }
    181 
    182     public void forAllPackageItems(Consumer<? super ShortcutPackageItem> callback) {
    183         forAllLaunchers(callback);
    184         forAllPackages(callback);
    185     }
    186 
    187     public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId,
    188             Consumer<ShortcutPackageItem> callback) {
    189         forAllPackageItems(spi -> {
    190             if ((spi.getPackageUserId() == packageUserId)
    191                     && spi.getPackageName().equals(packageName)) {
    192                 callback.accept(spi);
    193             }
    194         });
    195     }
    196 
    197     /**
    198      * Reset all throttling counters for all packages, if there has been a system locale change.
    199      */
    200     public void resetThrottlingIfNeeded(ShortcutService s) {
    201         final long currentNo = s.getLocaleChangeSequenceNumber();
    202         if (mKnownLocaleChangeSequenceNumber < currentNo) {
    203             if (ShortcutService.DEBUG) {
    204                 Slog.d(TAG, "LocaleChange detected for user " + mUserId);
    205             }
    206 
    207             mKnownLocaleChangeSequenceNumber = currentNo;
    208 
    209             forAllPackages(p -> p.resetRateLimiting(s));
    210 
    211             s.scheduleSaveUser(mUserId);
    212         }
    213     }
    214 
    215     /**
    216      * Called when a package is updated.
    217      */
    218     public void handlePackageUpdated(ShortcutService s, @NonNull String packageName,
    219             int newVersionCode) {
    220         if (!mPackages.containsKey(packageName)) {
    221             return;
    222         }
    223         getPackageShortcuts(s, packageName).handlePackageUpdated(s, newVersionCode);
    224     }
    225 
    226     public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
    227             @UserIdInt int packageUserId) {
    228         forPackageItem(packageName, packageUserId, spi -> {
    229             spi.attemptToRestoreIfNeededAndSave(s);
    230         });
    231     }
    232 
    233     public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
    234             throws IOException, XmlPullParserException {
    235         out.startTag(null, TAG_ROOT);
    236 
    237         ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER,
    238                 mKnownLocaleChangeSequenceNumber);
    239 
    240         ShortcutService.writeTagValue(out, TAG_LAUNCHER,
    241                 mLauncherComponent);
    242 
    243         // Can't use forEachPackageItem due to the checked exceptions.
    244         {
    245             final int size = mLaunchers.size();
    246             for (int i = 0; i < size; i++) {
    247                 saveShortcutPackageItem(s, out, mLaunchers.valueAt(i), forBackup);
    248             }
    249         }
    250         {
    251             final int size = mPackages.size();
    252             for (int i = 0; i < size; i++) {
    253                 saveShortcutPackageItem(s, out, mPackages.valueAt(i), forBackup);
    254             }
    255         }
    256 
    257         out.endTag(null, TAG_ROOT);
    258     }
    259 
    260     private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out,
    261             ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
    262         if (forBackup) {
    263             if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
    264                 return; // Don't save.
    265             }
    266             if (spi.getPackageUserId() != spi.getOwnerUserId()) {
    267                 return; // Don't save cross-user information.
    268             }
    269         }
    270         spi.saveToXml(out, forBackup);
    271     }
    272 
    273     public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
    274             boolean fromBackup) throws IOException, XmlPullParserException {
    275         final ShortcutUser ret = new ShortcutUser(userId);
    276 
    277         ret.mKnownLocaleChangeSequenceNumber = ShortcutService.parseLongAttribute(parser,
    278                 ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER);
    279 
    280         final int outerDepth = parser.getDepth();
    281         int type;
    282         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    283                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    284             if (type != XmlPullParser.START_TAG) {
    285                 continue;
    286             }
    287             final int depth = parser.getDepth();
    288             final String tag = parser.getName();
    289 
    290             if (depth == outerDepth + 1) {
    291                 switch (tag) {
    292                     case TAG_LAUNCHER: {
    293                         ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
    294                                 parser, ATTR_VALUE);
    295                         continue;
    296                     }
    297                     case ShortcutPackage.TAG_ROOT: {
    298                         final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
    299                                 s, ret, parser, fromBackup);
    300 
    301                         // Don't use addShortcut(), we don't need to save the icon.
    302                         ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
    303                         continue;
    304                     }
    305 
    306                     case ShortcutLauncher.TAG_ROOT: {
    307                         ret.addLauncher(
    308                                 ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
    309                         continue;
    310                     }
    311                 }
    312             }
    313             ShortcutService.warnForInvalidTag(depth, tag);
    314         }
    315         return ret;
    316     }
    317 
    318     public ComponentName getLauncherComponent() {
    319         return mLauncherComponent;
    320     }
    321 
    322     public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
    323         if (Objects.equal(mLauncherComponent, launcherComponent)) {
    324             return;
    325         }
    326         mLauncherComponent = launcherComponent;
    327         s.scheduleSaveUser(mUserId);
    328     }
    329 
    330     public void resetThrottling() {
    331         for (int i = mPackages.size() - 1; i >= 0; i--) {
    332             mPackages.valueAt(i).resetThrottling();
    333         }
    334     }
    335 
    336     public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
    337         pw.print(prefix);
    338         pw.print("User: ");
    339         pw.print(mUserId);
    340         pw.print("  Known locale seq#: ");
    341         pw.print(mKnownLocaleChangeSequenceNumber);
    342         pw.println();
    343 
    344         prefix += prefix + "  ";
    345 
    346         pw.print(prefix);
    347         pw.print("Default launcher: ");
    348         pw.print(mLauncherComponent);
    349         pw.println();
    350 
    351         for (int i = 0; i < mLaunchers.size(); i++) {
    352             mLaunchers.valueAt(i).dump(s, pw, prefix);
    353         }
    354 
    355         for (int i = 0; i < mPackages.size(); i++) {
    356             mPackages.valueAt(i).dump(s, pw, prefix);
    357         }
    358 
    359         pw.println();
    360         pw.print(prefix);
    361         pw.println("Bitmap directories: ");
    362         dumpDirectorySize(s, pw, prefix + "  ", s.getUserBitmapFilePath(mUserId));
    363     }
    364 
    365     private void dumpDirectorySize(@NonNull ShortcutService s, @NonNull PrintWriter pw,
    366             @NonNull String prefix, File path) {
    367         int numFiles = 0;
    368         long size = 0;
    369         final File[] children = path.listFiles();
    370         if (children != null) {
    371             for (File child : path.listFiles()) {
    372                 if (child.isFile()) {
    373                     numFiles++;
    374                     size += child.length();
    375                 } else if (child.isDirectory()) {
    376                     dumpDirectorySize(s, pw, prefix + "  ", child);
    377                 }
    378             }
    379         }
    380         pw.print(prefix);
    381         pw.print("Path: ");
    382         pw.print(path.getName());
    383         pw.print("/ has ");
    384         pw.print(numFiles);
    385         pw.print(" files, size=");
    386         pw.print(size);
    387         pw.print(" (");
    388         pw.print(Formatter.formatFileSize(s.mContext, size));
    389         pw.println(")");
    390     }
    391 }
    392