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