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.pm.PackageInfo;
     21 import android.content.pm.ShortcutInfo;
     22 import android.util.ArrayMap;
     23 import android.util.ArraySet;
     24 import android.util.Slog;
     25 
     26 import com.android.internal.annotations.VisibleForTesting;
     27 import com.android.server.pm.ShortcutUser.PackageWithUser;
     28 
     29 import org.json.JSONException;
     30 import org.json.JSONObject;
     31 import org.xmlpull.v1.XmlPullParser;
     32 import org.xmlpull.v1.XmlPullParserException;
     33 import org.xmlpull.v1.XmlSerializer;
     34 
     35 import java.io.IOException;
     36 import java.io.PrintWriter;
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 
     40 /**
     41  * Launcher information used by {@link ShortcutService}.
     42  *
     43  * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
     44  */
     45 class ShortcutLauncher extends ShortcutPackageItem {
     46     private static final String TAG = ShortcutService.TAG;
     47 
     48     static final String TAG_ROOT = "launcher-pins";
     49 
     50     private static final String TAG_PACKAGE = "package";
     51     private static final String TAG_PIN = "pin";
     52 
     53     private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
     54     private static final String ATTR_VALUE = "value";
     55     private static final String ATTR_PACKAGE_NAME = "package-name";
     56     private static final String ATTR_PACKAGE_USER_ID = "package-user";
     57 
     58     private final int mOwnerUserId;
     59 
     60     /**
     61      * Package name -> IDs.
     62      */
     63     final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
     64 
     65     private ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
     66             @UserIdInt int ownerUserId, @NonNull String packageName,
     67             @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
     68         super(shortcutUser, launcherUserId, packageName,
     69                 spi != null ? spi : ShortcutPackageInfo.newEmpty());
     70         mOwnerUserId = ownerUserId;
     71     }
     72 
     73     public ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
     74             @UserIdInt int ownerUserId, @NonNull String packageName,
     75             @UserIdInt int launcherUserId) {
     76         this(shortcutUser, ownerUserId, packageName, launcherUserId, null);
     77     }
     78 
     79     @Override
     80     public int getOwnerUserId() {
     81         return mOwnerUserId;
     82     }
     83 
     84     /**
     85      * Called when the new package can't receive the backup, due to signature or version mismatch.
     86      */
     87     @Override
     88     protected void onRestoreBlocked() {
     89         final ArrayList<PackageWithUser> pinnedPackages =
     90                 new ArrayList<>(mPinnedShortcuts.keySet());
     91         mPinnedShortcuts.clear();
     92         for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
     93             final PackageWithUser pu = pinnedPackages.get(i);
     94             final ShortcutPackage p = mShortcutUser.getPackageShortcutsIfExists(pu.packageName);
     95             if (p != null) {
     96                 p.refreshPinnedFlags();
     97             }
     98         }
     99     }
    100 
    101     @Override
    102     protected void onRestored() {
    103         // Nothing to do.
    104     }
    105 
    106     public void pinShortcuts(@UserIdInt int packageUserId,
    107             @NonNull String packageName, @NonNull List<String> ids) {
    108         final ShortcutPackage packageShortcuts =
    109                 mShortcutUser.getPackageShortcutsIfExists(packageName);
    110         if (packageShortcuts == null) {
    111             return; // No need to instantiate.
    112         }
    113 
    114         final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
    115 
    116         final int idSize = ids.size();
    117         if (idSize == 0) {
    118             mPinnedShortcuts.remove(pu);
    119         } else {
    120             final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
    121 
    122             // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
    123             // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
    124 
    125             final ArraySet<String> newSet = new ArraySet<>();
    126 
    127             for (int i = 0; i < idSize; i++) {
    128                 final String id = ids.get(i);
    129                 final ShortcutInfo si = packageShortcuts.findShortcutById(id);
    130                 if (si == null) {
    131                     continue;
    132                 }
    133                 if (si.isDynamic() || si.isManifestShortcut()
    134                         || (prevSet != null && prevSet.contains(id))) {
    135                     newSet.add(id);
    136                 }
    137             }
    138             mPinnedShortcuts.put(pu, newSet);
    139         }
    140         packageShortcuts.refreshPinnedFlags();
    141     }
    142 
    143     /**
    144      * Return the pinned shortcut IDs for the publisher package.
    145      */
    146     public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
    147             @UserIdInt int packageUserId) {
    148         return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
    149     }
    150 
    151     boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
    152         return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
    153     }
    154 
    155     public void ensureVersionInfo() {
    156         final PackageInfo pi = mShortcutUser.mService.getPackageInfoWithSignatures(
    157                 getPackageName(), getPackageUserId());
    158         if (pi == null) {
    159             Slog.w(TAG, "Package not found: " + getPackageName());
    160             return;
    161         }
    162         getPackageInfo().updateVersionInfo(pi);
    163     }
    164 
    165     /**
    166      * Persist.
    167      */
    168     @Override
    169     public void saveToXml(XmlSerializer out, boolean forBackup)
    170             throws IOException {
    171         final int size = mPinnedShortcuts.size();
    172         if (size == 0) {
    173             return; // Nothing to write.
    174         }
    175 
    176         out.startTag(null, TAG_ROOT);
    177         ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
    178         ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
    179         getPackageInfo().saveToXml(out);
    180 
    181         for (int i = 0; i < size; i++) {
    182             final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
    183 
    184             if (forBackup && (pu.userId != getOwnerUserId())) {
    185                 continue; // Target package on a different user, skip. (i.e. work profile)
    186             }
    187 
    188             out.startTag(null, TAG_PACKAGE);
    189             ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName);
    190             ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId);
    191 
    192             final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
    193             final int idSize = ids.size();
    194             for (int j = 0; j < idSize; j++) {
    195                 ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
    196             }
    197             out.endTag(null, TAG_PACKAGE);
    198         }
    199 
    200         out.endTag(null, TAG_ROOT);
    201     }
    202 
    203     /**
    204      * Load.
    205      */
    206     public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
    207             int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
    208         final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
    209                 ATTR_PACKAGE_NAME);
    210 
    211         // If restoring, just use the real user ID.
    212         final int launcherUserId =
    213                 fromBackup ? ownerUserId
    214                 : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
    215 
    216         final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, ownerUserId,
    217                 launcherPackageName, launcherUserId);
    218 
    219         ArraySet<String> ids = null;
    220         final int outerDepth = parser.getDepth();
    221         int type;
    222         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    223                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    224             if (type != XmlPullParser.START_TAG) {
    225                 continue;
    226             }
    227             final int depth = parser.getDepth();
    228             final String tag = parser.getName();
    229             if (depth == outerDepth + 1) {
    230                 switch (tag) {
    231                     case ShortcutPackageInfo.TAG_ROOT:
    232                         ret.getPackageInfo().loadFromXml(parser, fromBackup);
    233                         continue;
    234                     case TAG_PACKAGE: {
    235                         final String packageName = ShortcutService.parseStringAttribute(parser,
    236                                 ATTR_PACKAGE_NAME);
    237                         final int packageUserId = fromBackup ? ownerUserId
    238                                 : ShortcutService.parseIntAttribute(parser,
    239                                 ATTR_PACKAGE_USER_ID, ownerUserId);
    240                         ids = new ArraySet<>();
    241                         ret.mPinnedShortcuts.put(
    242                                 PackageWithUser.of(packageUserId, packageName), ids);
    243                         continue;
    244                     }
    245                 }
    246             }
    247             if (depth == outerDepth + 2) {
    248                 switch (tag) {
    249                     case TAG_PIN: {
    250                         if (ids == null) {
    251                             Slog.w(TAG, TAG_PIN + " in invalid place");
    252                         } else {
    253                             ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE));
    254                         }
    255                         continue;
    256                     }
    257                 }
    258             }
    259             ShortcutService.warnForInvalidTag(depth, tag);
    260         }
    261         return ret;
    262     }
    263 
    264     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
    265         pw.println();
    266 
    267         pw.print(prefix);
    268         pw.print("Launcher: ");
    269         pw.print(getPackageName());
    270         pw.print("  Package user: ");
    271         pw.print(getPackageUserId());
    272         pw.print("  Owner user: ");
    273         pw.print(getOwnerUserId());
    274         pw.println();
    275 
    276         getPackageInfo().dump(pw, prefix + "  ");
    277         pw.println();
    278 
    279         final int size = mPinnedShortcuts.size();
    280         for (int i = 0; i < size; i++) {
    281             pw.println();
    282 
    283             final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
    284 
    285             pw.print(prefix);
    286             pw.print("  ");
    287             pw.print("Package: ");
    288             pw.print(pu.packageName);
    289             pw.print("  User: ");
    290             pw.println(pu.userId);
    291 
    292             final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
    293             final int idSize = ids.size();
    294 
    295             for (int j = 0; j < idSize; j++) {
    296                 pw.print(prefix);
    297                 pw.print("    Pinned: ");
    298                 pw.print(ids.valueAt(j));
    299                 pw.println();
    300             }
    301         }
    302     }
    303 
    304     @Override
    305     public JSONObject dumpCheckin(boolean clear) throws JSONException {
    306         final JSONObject result = super.dumpCheckin(clear);
    307 
    308         // Nothing really interesting to dump.
    309 
    310         return result;
    311     }
    312 
    313     @VisibleForTesting
    314     ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
    315         return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
    316     }
    317 }
    318