blob: e667838ad5e554d9fac4c5f233b069397516941b [file] [log] [blame]
Makoto Onuki31459242016-03-22 11:12:18 -07001/*
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 */
16package com.android.server.pm;
17
18import android.annotation.NonNull;
19import android.annotation.UserIdInt;
20import android.content.pm.ShortcutInfo;
21import android.util.ArrayMap;
22import android.util.ArraySet;
Makoto Onuki2e210c42016-03-30 08:30:36 -070023import android.util.Slog;
24
25import com.android.internal.annotations.VisibleForTesting;
26import com.android.server.pm.ShortcutUser.PackageWithUser;
Makoto Onuki31459242016-03-22 11:12:18 -070027
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.IOException;
33import java.io.PrintWriter;
Makoto Onuki2e210c42016-03-30 08:30:36 -070034import java.util.ArrayList;
Makoto Onuki31459242016-03-22 11:12:18 -070035import java.util.List;
36
37/**
38 * Launcher information used by {@link ShortcutService}.
Makoto Onuki22fcc682016-05-17 14:52:19 -070039 *
40 * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
Makoto Onuki31459242016-03-22 11:12:18 -070041 */
Makoto Onuki9da23fc2016-03-29 11:14:42 -070042class ShortcutLauncher extends ShortcutPackageItem {
Makoto Onuki31459242016-03-22 11:12:18 -070043 private static final String TAG = ShortcutService.TAG;
44
45 static final String TAG_ROOT = "launcher-pins";
46
47 private static final String TAG_PACKAGE = "package";
48 private static final String TAG_PIN = "pin";
49
Makoto Onukid99c6f02016-03-28 11:02:54 -070050 private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
Makoto Onuki31459242016-03-22 11:12:18 -070051 private static final String ATTR_VALUE = "value";
52 private static final String ATTR_PACKAGE_NAME = "package-name";
Makoto Onuki2e210c42016-03-30 08:30:36 -070053 private static final String ATTR_PACKAGE_USER_ID = "package-user";
Makoto Onuki31459242016-03-22 11:12:18 -070054
Makoto Onuki9da23fc2016-03-29 11:14:42 -070055 private final int mOwnerUserId;
Makoto Onukid99c6f02016-03-28 11:02:54 -070056
Makoto Onuki31459242016-03-22 11:12:18 -070057 /**
58 * Package name -> IDs.
59 */
Makoto Onuki2e210c42016-03-30 08:30:36 -070060 final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
Makoto Onuki31459242016-03-22 11:12:18 -070061
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070062 private ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
63 @UserIdInt int ownerUserId, @NonNull String packageName,
Makoto Onuki9da23fc2016-03-29 11:14:42 -070064 @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070065 super(shortcutUser, launcherUserId, packageName,
66 spi != null ? spi : ShortcutPackageInfo.newEmpty());
Makoto Onuki9da23fc2016-03-29 11:14:42 -070067 mOwnerUserId = ownerUserId;
68 }
69
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070070 public ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
71 @UserIdInt int ownerUserId, @NonNull String packageName,
Makoto Onukid99c6f02016-03-28 11:02:54 -070072 @UserIdInt int launcherUserId) {
Makoto Onuki4d36b3a2016-04-27 12:00:17 -070073 this(shortcutUser, ownerUserId, packageName, launcherUserId, null);
Makoto Onuki31459242016-03-22 11:12:18 -070074 }
75
Makoto Onuki9da23fc2016-03-29 11:14:42 -070076 @Override
77 public int getOwnerUserId() {
78 return mOwnerUserId;
Makoto Onuki0acbb142016-03-22 17:02:57 -070079 }
80
Makoto Onuki2e210c42016-03-30 08:30:36 -070081 /**
82 * Called when the new package can't receive the backup, due to signature or version mismatch.
83 */
84 @Override
Makoto Onukic51b2872016-05-04 15:24:50 -070085 protected void onRestoreBlocked() {
Makoto Onuki2e210c42016-03-30 08:30:36 -070086 final ArrayList<PackageWithUser> pinnedPackages =
87 new ArrayList<>(mPinnedShortcuts.keySet());
88 mPinnedShortcuts.clear();
89 for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
90 final PackageWithUser pu = pinnedPackages.get(i);
Makoto Onukic51b2872016-05-04 15:24:50 -070091 final ShortcutPackage p = mShortcutUser.getPackageShortcutsIfExists(pu.packageName);
92 if (p != null) {
93 p.refreshPinnedFlags();
94 }
Makoto Onuki2e210c42016-03-30 08:30:36 -070095 }
96 }
97
98 @Override
Makoto Onukic51b2872016-05-04 15:24:50 -070099 protected void onRestored() {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700100 // Nothing to do.
101 }
102
Makoto Onukic51b2872016-05-04 15:24:50 -0700103 public void pinShortcuts(@UserIdInt int packageUserId,
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700104 @NonNull String packageName, @NonNull List<String> ids) {
105 final ShortcutPackage packageShortcuts =
Makoto Onukic51b2872016-05-04 15:24:50 -0700106 mShortcutUser.getPackageShortcutsIfExists(packageName);
107 if (packageShortcuts == null) {
108 return; // No need to instantiate.
109 }
Makoto Onukid99c6f02016-03-28 11:02:54 -0700110
Makoto Onuki2e210c42016-03-30 08:30:36 -0700111 final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
112
Makoto Onuki31459242016-03-22 11:12:18 -0700113 final int idSize = ids.size();
114 if (idSize == 0) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700115 mPinnedShortcuts.remove(pu);
Makoto Onuki31459242016-03-22 11:12:18 -0700116 } else {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700117 final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
Makoto Onuki31459242016-03-22 11:12:18 -0700118
119 // Pin shortcuts. Make sure only pin the ones that were visible to the caller.
120 // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
121
Makoto Onuki31459242016-03-22 11:12:18 -0700122 final ArraySet<String> newSet = new ArraySet<>();
123
124 for (int i = 0; i < idSize; i++) {
125 final String id = ids.get(i);
126 final ShortcutInfo si = packageShortcuts.findShortcutById(id);
127 if (si == null) {
128 continue;
129 }
Makoto Onuki22fcc682016-05-17 14:52:19 -0700130 if (si.isDynamic() || si.isManifestShortcut()
131 || (prevSet != null && prevSet.contains(id))) {
Makoto Onuki31459242016-03-22 11:12:18 -0700132 newSet.add(id);
133 }
134 }
Makoto Onuki2e210c42016-03-30 08:30:36 -0700135 mPinnedShortcuts.put(pu, newSet);
Makoto Onuki31459242016-03-22 11:12:18 -0700136 }
Makoto Onukic51b2872016-05-04 15:24:50 -0700137 packageShortcuts.refreshPinnedFlags();
Makoto Onuki31459242016-03-22 11:12:18 -0700138 }
139
140 /**
141 * Return the pinned shortcut IDs for the publisher package.
142 */
Makoto Onuki2e210c42016-03-30 08:30:36 -0700143 public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
144 @UserIdInt int packageUserId) {
145 return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
Makoto Onuki31459242016-03-22 11:12:18 -0700146 }
147
Makoto Onuki2e210c42016-03-30 08:30:36 -0700148 boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
149 return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
Makoto Onuki31459242016-03-22 11:12:18 -0700150 }
151
152 /**
153 * Persist.
154 */
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700155 @Override
156 public void saveToXml(XmlSerializer out, boolean forBackup)
157 throws IOException {
Makoto Onuki31459242016-03-22 11:12:18 -0700158 final int size = mPinnedShortcuts.size();
159 if (size == 0) {
160 return; // Nothing to write.
161 }
162
163 out.startTag(null, TAG_ROOT);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700164 ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
165 ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
166 getPackageInfo().saveToXml(out);
Makoto Onuki31459242016-03-22 11:12:18 -0700167
168 for (int i = 0; i < size; i++) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700169 final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
170
171 if (forBackup && (pu.userId != getOwnerUserId())) {
172 continue; // Target package on a different user, skip. (i.e. work profile)
173 }
174
Makoto Onuki31459242016-03-22 11:12:18 -0700175 out.startTag(null, TAG_PACKAGE);
Makoto Onuki2e210c42016-03-30 08:30:36 -0700176 ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName);
177 ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId);
Makoto Onuki31459242016-03-22 11:12:18 -0700178
179 final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
180 final int idSize = ids.size();
181 for (int j = 0; j < idSize; j++) {
182 ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
183 }
184 out.endTag(null, TAG_PACKAGE);
185 }
186
187 out.endTag(null, TAG_ROOT);
188 }
189
190 /**
191 * Load.
192 */
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700193 public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
194 int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
Makoto Onuki31459242016-03-22 11:12:18 -0700195 final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
196 ATTR_PACKAGE_NAME);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700197
198 // If restoring, just use the real user ID.
199 final int launcherUserId =
200 fromBackup ? ownerUserId
201 : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
Makoto Onuki31459242016-03-22 11:12:18 -0700202
Makoto Onuki4d36b3a2016-04-27 12:00:17 -0700203 final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, launcherUserId,
204 launcherPackageName, launcherUserId);
Makoto Onuki31459242016-03-22 11:12:18 -0700205
206 ArraySet<String> ids = null;
207 final int outerDepth = parser.getDepth();
208 int type;
209 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
210 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
211 if (type != XmlPullParser.START_TAG) {
212 continue;
213 }
214 final int depth = parser.getDepth();
215 final String tag = parser.getName();
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700216 if (depth == outerDepth + 1) {
217 switch (tag) {
218 case ShortcutPackageInfo.TAG_ROOT:
Makoto Onuki2e210c42016-03-30 08:30:36 -0700219 ret.getPackageInfo().loadFromXml(parser, fromBackup);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700220 continue;
221 case TAG_PACKAGE: {
222 final String packageName = ShortcutService.parseStringAttribute(parser,
223 ATTR_PACKAGE_NAME);
Makoto Onuki2e210c42016-03-30 08:30:36 -0700224 final int packageUserId = fromBackup ? ownerUserId
225 : ShortcutService.parseIntAttribute(parser,
226 ATTR_PACKAGE_USER_ID, ownerUserId);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700227 ids = new ArraySet<>();
Makoto Onuki2e210c42016-03-30 08:30:36 -0700228 ret.mPinnedShortcuts.put(
229 PackageWithUser.of(packageUserId, packageName), ids);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700230 continue;
231 }
Makoto Onuki31459242016-03-22 11:12:18 -0700232 }
233 }
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700234 if (depth == outerDepth + 2) {
235 switch (tag) {
236 case TAG_PIN: {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700237 if (ids == null) {
238 Slog.w(TAG, TAG_PIN + " in invalid place");
239 } else {
240 ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE));
241 }
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700242 continue;
243 }
244 }
245 }
246 ShortcutService.warnForInvalidTag(depth, tag);
247 }
Makoto Onuki31459242016-03-22 11:12:18 -0700248 return ret;
249 }
250
Makoto Onukic51b2872016-05-04 15:24:50 -0700251 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
Makoto Onuki31459242016-03-22 11:12:18 -0700252 pw.println();
253
254 pw.print(prefix);
255 pw.print("Launcher: ");
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700256 pw.print(getPackageName());
257 pw.print(" Package user: ");
258 pw.print(getPackageUserId());
Makoto Onuki2e210c42016-03-30 08:30:36 -0700259 pw.print(" Owner user: ");
260 pw.print(getOwnerUserId());
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700261 pw.println();
262
Makoto Onukic51b2872016-05-04 15:24:50 -0700263 getPackageInfo().dump(pw, prefix + " ");
Makoto Onuki31459242016-03-22 11:12:18 -0700264 pw.println();
265
266 final int size = mPinnedShortcuts.size();
267 for (int i = 0; i < size; i++) {
268 pw.println();
269
Makoto Onuki2e210c42016-03-30 08:30:36 -0700270 final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
271
Makoto Onuki31459242016-03-22 11:12:18 -0700272 pw.print(prefix);
273 pw.print(" ");
274 pw.print("Package: ");
Makoto Onuki2e210c42016-03-30 08:30:36 -0700275 pw.print(pu.packageName);
276 pw.print(" User: ");
277 pw.println(pu.userId);
Makoto Onuki31459242016-03-22 11:12:18 -0700278
279 final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
280 final int idSize = ids.size();
281
282 for (int j = 0; j < idSize; j++) {
283 pw.print(prefix);
Makoto Onuki0acbb142016-03-22 17:02:57 -0700284 pw.print(" Pinned: ");
Makoto Onuki31459242016-03-22 11:12:18 -0700285 pw.print(ids.valueAt(j));
286 pw.println();
287 }
288 }
289 }
Makoto Onuki2e210c42016-03-30 08:30:36 -0700290
291 @VisibleForTesting
292 ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
293 return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
294 }
Makoto Onuki31459242016-03-22 11:12:18 -0700295}