• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShortcutLauncher(@onNull ShortcutUser shortcutUser, @UserIdInt int ownerUserId, @NonNull String packageName, @UserIdInt int launcherUserId, ShortcutPackageInfo spi)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 
ShortcutLauncher(@onNull ShortcutUser shortcutUser, @UserIdInt int ownerUserId, @NonNull String packageName, @UserIdInt int launcherUserId)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
getOwnerUserId()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
onRestoreBlocked()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
onRestored()101     protected void onRestored() {
102         // Nothing to do.
103     }
104 
pinShortcuts(@serIdInt int packageUserId, @NonNull String packageName, @NonNull List<String> ids)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      */
getPinnedShortcutIds(@onNull String packageName, @UserIdInt int packageUserId)145     public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
146             @UserIdInt int packageUserId) {
147         return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
148     }
149 
cleanUpPackage(String packageName, @UserIdInt int packageUserId)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
saveToXml(XmlSerializer out, boolean forBackup)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      */
loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser, int ownerUserId, boolean fromBackup)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 
dump(@onNull PrintWriter pw, @NonNull String prefix)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
dumpCheckin(boolean clear)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
getAllPinnedShortcutsForTest(String packageName, int packageUserId)303     ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
304         return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
305     }
306 }
307