blob: 1076a7a0ae8837f2e1b227f12a8837bcc3a05b5b [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.Nullable;
Makoto Onuki31459242016-03-22 11:12:18 -070020import android.content.ComponentName;
21import android.content.Intent;
22import android.content.pm.ShortcutInfo;
23import android.os.PersistableBundle;
24import android.text.format.Formatter;
25import android.util.ArrayMap;
26import android.util.ArraySet;
27import android.util.Slog;
28
Makoto Onuki2e210c42016-03-30 08:30:36 -070029import com.android.internal.annotations.VisibleForTesting;
30
Makoto Onuki31459242016-03-22 11:12:18 -070031import org.xmlpull.v1.XmlPullParser;
32import org.xmlpull.v1.XmlPullParserException;
33import org.xmlpull.v1.XmlSerializer;
34
35import java.io.File;
36import java.io.IOException;
37import java.io.PrintWriter;
38import java.util.ArrayList;
Makoto Onuki2e210c42016-03-30 08:30:36 -070039import java.util.Collection;
Makoto Onuki31459242016-03-22 11:12:18 -070040import java.util.List;
41import java.util.function.Predicate;
42
43/**
44 * Package information used by {@link ShortcutService}.
45 */
Makoto Onuki9da23fc2016-03-29 11:14:42 -070046class ShortcutPackage extends ShortcutPackageItem {
Makoto Onuki31459242016-03-22 11:12:18 -070047 private static final String TAG = ShortcutService.TAG;
48
49 static final String TAG_ROOT = "package";
50 private static final String TAG_INTENT_EXTRAS = "intent-extras";
51 private static final String TAG_EXTRAS = "extras";
52 private static final String TAG_SHORTCUT = "shortcut";
53
54 private static final String ATTR_NAME = "name";
55 private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
56 private static final String ATTR_CALL_COUNT = "call-count";
57 private static final String ATTR_LAST_RESET = "last-reset";
58 private static final String ATTR_ID = "id";
59 private static final String ATTR_ACTIVITY = "activity";
60 private static final String ATTR_TITLE = "title";
Makoto Onukie3ae7ec2016-03-29 15:45:25 -070061 private static final String ATTR_TEXT = "text";
Makoto Onuki31459242016-03-22 11:12:18 -070062 private static final String ATTR_INTENT = "intent";
63 private static final String ATTR_WEIGHT = "weight";
64 private static final String ATTR_TIMESTAMP = "timestamp";
65 private static final String ATTR_FLAGS = "flags";
66 private static final String ATTR_ICON_RES = "icon-res";
67 private static final String ATTR_BITMAP_PATH = "bitmap-path";
68
Makoto Onuki31459242016-03-22 11:12:18 -070069 /**
70 * All the shortcuts from the package, keyed on IDs.
71 */
72 final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
73
74 /**
75 * # of dynamic shortcuts.
76 */
77 private int mDynamicShortcutCount = 0;
78
79 /**
80 * # of times the package has called rate-limited APIs.
81 */
82 private int mApiCallCount;
83
84 /**
85 * When {@link #mApiCallCount} was reset last time.
86 */
87 private long mLastResetTime;
88
Makoto Onuki2e210c42016-03-30 08:30:36 -070089 private ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -070090 super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
Makoto Onuki31459242016-03-22 11:12:18 -070091 }
92
Makoto Onuki9da23fc2016-03-29 11:14:42 -070093 public ShortcutPackage(int packageUserId, String packageName) {
94 this(packageUserId, packageName, null);
Makoto Onuki0acbb142016-03-22 17:02:57 -070095 }
96
Makoto Onuki9da23fc2016-03-29 11:14:42 -070097 @Override
98 public int getOwnerUserId() {
99 // For packages, always owner user == package user.
100 return getPackageUserId();
Makoto Onuki0acbb142016-03-22 17:02:57 -0700101 }
102
Makoto Onuki2e210c42016-03-30 08:30:36 -0700103 @Override
104 protected void onRestoreBlocked(ShortcutService s) {
105 // Can't restore due to version/signature mismatch. Remove all shortcuts.
106 mShortcuts.clear();
107 }
108
109 @Override
110 protected void onRestored(ShortcutService s) {
111 // Because some launchers may not have been restored (e.g. allowBackup=false),
112 // we need to re-calculate the pinned shortcuts.
113 refreshPinnedFlags(s);
114 }
115
Makoto Onukid99c6f02016-03-28 11:02:54 -0700116 /**
117 * Note this does *not* provide a correct view to the calling launcher.
118 */
Makoto Onuki31459242016-03-22 11:12:18 -0700119 @Nullable
120 public ShortcutInfo findShortcutById(String id) {
121 return mShortcuts.get(id);
122 }
123
124 private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
125 @NonNull String id) {
126 final ShortcutInfo shortcut = mShortcuts.remove(id);
127 if (shortcut != null) {
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700128 s.removeIcon(getPackageUserId(), shortcut);
Makoto Onuki31459242016-03-22 11:12:18 -0700129 shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
130 }
131 return shortcut;
132 }
133
134 void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
135 deleteShortcut(s, newShortcut.getId());
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700136 s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
Makoto Onuki31459242016-03-22 11:12:18 -0700137 mShortcuts.put(newShortcut.getId(), newShortcut);
138 }
139
140 /**
141 * Add a shortcut, or update one with the same ID, with taking over existing flags.
142 *
143 * It checks the max number of dynamic shortcuts.
144 */
145 public void addDynamicShortcut(@NonNull ShortcutService s,
146 @NonNull ShortcutInfo newShortcut) {
147 newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
148
149 final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
150
151 final boolean wasPinned;
152 final int newDynamicCount;
153
154 if (oldShortcut == null) {
155 wasPinned = false;
156 newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
157 } else {
158 wasPinned = oldShortcut.isPinned();
159 if (oldShortcut.isDynamic()) {
160 newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
161 } else {
162 newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
163 }
164 }
165
166 // Make sure there's still room.
167 s.enforceMaxDynamicShortcuts(newDynamicCount);
168
169 // Okay, make it dynamic and add.
170 if (wasPinned) {
171 newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
172 }
173
174 addShortcut(s, newShortcut);
175 mDynamicShortcutCount = newDynamicCount;
176 }
177
178 /**
179 * Remove all shortcuts that aren't pinned nor dynamic.
180 */
181 private void removeOrphans(@NonNull ShortcutService s) {
182 ArrayList<String> removeList = null; // Lazily initialize.
183
184 for (int i = mShortcuts.size() - 1; i >= 0; i--) {
185 final ShortcutInfo si = mShortcuts.valueAt(i);
186
187 if (si.isPinned() || si.isDynamic()) continue;
188
189 if (removeList == null) {
190 removeList = new ArrayList<>();
191 }
192 removeList.add(si.getId());
193 }
194 if (removeList != null) {
195 for (int i = removeList.size() - 1; i >= 0; i--) {
196 deleteShortcut(s, removeList.get(i));
197 }
198 }
199 }
200
201 /**
202 * Remove all dynamic shortcuts.
203 */
204 public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
205 for (int i = mShortcuts.size() - 1; i >= 0; i--) {
206 mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
207 }
208 removeOrphans(s);
209 mDynamicShortcutCount = 0;
210 }
211
212 /**
213 * Remove a dynamic shortcut by ID.
214 */
215 public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
216 final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
217
218 if (oldShortcut == null) {
219 return;
220 }
221 if (oldShortcut.isDynamic()) {
222 mDynamicShortcutCount--;
223 }
224 if (oldShortcut.isPinned()) {
225 oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
226 } else {
227 deleteShortcut(s, shortcutId);
228 }
229 }
230
231 /**
232 * Called after a launcher updates the pinned set. For each shortcut in this package,
233 * set FLAG_PINNED if any launcher has pinned it. Otherwise, clear it.
234 *
235 * <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
236 */
237 public void refreshPinnedFlags(@NonNull ShortcutService s) {
238 // First, un-pin all shortcuts
239 for (int i = mShortcuts.size() - 1; i >= 0; i--) {
240 mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
241 }
242
243 // Then, for the pinned set for each launcher, set the pin flag one by one.
Makoto Onukid99c6f02016-03-28 11:02:54 -0700244 final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700245 s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers();
Makoto Onuki31459242016-03-22 11:12:18 -0700246
247 for (int l = launchers.size() - 1; l >= 0; l--) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700248 // Note even if a launcher that hasn't been installed can still pin shortcuts.
249
Makoto Onuki31459242016-03-22 11:12:18 -0700250 final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700251 final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
Makoto Onuki2e210c42016-03-30 08:30:36 -0700252 getPackageName(), getPackageUserId());
Makoto Onuki31459242016-03-22 11:12:18 -0700253
254 if (pinned == null || pinned.size() == 0) {
255 continue;
256 }
257 for (int i = pinned.size() - 1; i >= 0; i--) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700258 final String id = pinned.valueAt(i);
259 final ShortcutInfo si = mShortcuts.get(id);
Makoto Onuki31459242016-03-22 11:12:18 -0700260 if (si == null) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700261 // This happens if a launcher pinned shortcuts from this package, then backup&
262 // restored, but this package doesn't allow backing up.
263 // In that case the launcher ends up having a dangling pinned shortcuts.
264 // That's fine, when the launcher is restored, we'll fix it.
265 continue;
Makoto Onuki31459242016-03-22 11:12:18 -0700266 }
Makoto Onuki2e210c42016-03-30 08:30:36 -0700267 si.addFlags(ShortcutInfo.FLAG_PINNED);
Makoto Onuki31459242016-03-22 11:12:18 -0700268 }
269 }
270
271 // Lastly, remove the ones that are no longer pinned nor dynamic.
272 removeOrphans(s);
273 }
274
275 /**
276 * Number of calls that the caller has made, since the last reset.
277 */
278 public int getApiCallCount(@NonNull ShortcutService s) {
279 final long last = s.getLastResetTimeLocked();
280
281 final long now = s.injectCurrentTimeMillis();
282 if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
283 Slog.w(TAG, "Clock rewound");
284 // Clock rewound.
285 mLastResetTime = now;
286 mApiCallCount = 0;
287 return mApiCallCount;
288 }
289
290 // If not reset yet, then reset.
291 if (mLastResetTime < last) {
292 if (ShortcutService.DEBUG) {
293 Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting",
294 mLastResetTime, now, last));
295 }
296 mApiCallCount = 0;
297 mLastResetTime = last;
298 }
299 return mApiCallCount;
300 }
301
302 /**
303 * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
304 * and return true. Otherwise just return false.
305 */
306 public boolean tryApiCall(@NonNull ShortcutService s) {
307 if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
308 return false;
309 }
310 mApiCallCount++;
311 return true;
312 }
313
314 public void resetRateLimitingForCommandLine() {
315 mApiCallCount = 0;
316 mLastResetTime = 0;
317 }
318
319 /**
320 * Find all shortcuts that match {@code query}.
321 */
322 public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
Makoto Onukid99c6f02016-03-28 11:02:54 -0700323 @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
324 findAll(s, result, query, cloneFlag, null, 0);
325 }
326
327 /**
328 * Find all shortcuts that match {@code query}.
329 *
330 * This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
331 * by the calling launcher will not be included in the result, and also "isPinned" will be
332 * adjusted for the caller too.
333 */
334 public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
Makoto Onuki31459242016-03-22 11:12:18 -0700335 @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
Makoto Onukid99c6f02016-03-28 11:02:54 -0700336 @Nullable String callingLauncher, int launcherUserId) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700337 if (getPackageInfo().isShadow()) {
338 // Restored and the app not installed yet, so don't return any.
339 return;
340 }
Makoto Onuki31459242016-03-22 11:12:18 -0700341
342 // Set of pinned shortcuts by the calling launcher.
343 final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
Makoto Onuki2e210c42016-03-30 08:30:36 -0700344 : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
345 .getPinnedShortcutIds(getPackageName(), getPackageUserId());
Makoto Onuki31459242016-03-22 11:12:18 -0700346
347 for (int i = 0; i < mShortcuts.size(); i++) {
348 final ShortcutInfo si = mShortcuts.valueAt(i);
349
350 // If it's called by non-launcher (i.e. publisher, always include -> true.
351 // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
352 // it.
353 final boolean isPinnedByCaller = (callingLauncher == null)
354 || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
355 if (!si.isDynamic()) {
356 if (!si.isPinned()) {
Makoto Onuki2e210c42016-03-30 08:30:36 -0700357 s.wtf("Shortcut not pinned: package " + getPackageName()
358 + ", user=" + getPackageUserId() + ", id=" + si.getId());
Makoto Onuki31459242016-03-22 11:12:18 -0700359 continue;
360 }
361 if (!isPinnedByCaller) {
362 continue;
363 }
364 }
365 final ShortcutInfo clone = si.clone(cloneFlag);
366 // Fix up isPinned for the caller. Note we need to do it before the "test" callback,
367 // since it may check isPinned.
368 if (!isPinnedByCaller) {
369 clone.clearFlags(ShortcutInfo.FLAG_PINNED);
370 }
371 if (query == null || query.test(clone)) {
372 result.add(clone);
373 }
374 }
375 }
376
377 public void resetThrottling() {
378 mApiCallCount = 0;
379 }
380
381 public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
382 pw.println();
383
384 pw.print(prefix);
385 pw.print("Package: ");
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700386 pw.print(getPackageName());
Makoto Onuki31459242016-03-22 11:12:18 -0700387 pw.println();
388
389 pw.print(prefix);
390 pw.print(" ");
391 pw.print("Calls: ");
392 pw.print(getApiCallCount(s));
393 pw.println();
394
395 // This should be after getApiCallCount(), which may update it.
396 pw.print(prefix);
397 pw.print(" ");
398 pw.print("Last reset: [");
399 pw.print(mLastResetTime);
400 pw.print("] ");
401 pw.print(s.formatTime(mLastResetTime));
402 pw.println();
403
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700404 getPackageInfo().dump(s, pw, prefix + " ");
405 pw.println();
406
Makoto Onuki31459242016-03-22 11:12:18 -0700407 pw.println(" Shortcuts:");
408 long totalBitmapSize = 0;
409 final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
410 final int size = shortcuts.size();
411 for (int i = 0; i < size; i++) {
412 final ShortcutInfo si = shortcuts.valueAt(i);
413 pw.print(" ");
414 pw.println(si.toInsecureString());
415 if (si.getBitmapPath() != null) {
416 final long len = new File(si.getBitmapPath()).length();
417 pw.print(" ");
418 pw.print("bitmap size=");
419 pw.println(len);
420
421 totalBitmapSize += len;
422 }
423 }
424 pw.print(prefix);
425 pw.print(" ");
426 pw.print("Total bitmap size: ");
427 pw.print(totalBitmapSize);
428 pw.print(" (");
429 pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
430 pw.println(")");
431 }
432
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700433 @Override
Makoto Onuki0acbb142016-03-22 17:02:57 -0700434 public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
435 throws IOException, XmlPullParserException {
Makoto Onuki31459242016-03-22 11:12:18 -0700436 final int size = mShortcuts.size();
437
438 if (size == 0 && mApiCallCount == 0) {
439 return; // nothing to write.
440 }
441
442 out.startTag(null, TAG_ROOT);
443
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700444 ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
Makoto Onuki31459242016-03-22 11:12:18 -0700445 ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
446 ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
447 ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700448 getPackageInfo().saveToXml(out);
Makoto Onuki31459242016-03-22 11:12:18 -0700449
450 for (int j = 0; j < size; j++) {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700451 saveShortcut(out, mShortcuts.valueAt(j), forBackup);
Makoto Onuki31459242016-03-22 11:12:18 -0700452 }
453
454 out.endTag(null, TAG_ROOT);
455 }
456
Makoto Onuki0acbb142016-03-22 17:02:57 -0700457 private static void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
Makoto Onuki31459242016-03-22 11:12:18 -0700458 throws IOException, XmlPullParserException {
Makoto Onuki0acbb142016-03-22 17:02:57 -0700459 if (forBackup) {
460 if (!si.isPinned()) {
461 return; // Backup only pinned icons.
462 }
463 }
Makoto Onuki31459242016-03-22 11:12:18 -0700464 out.startTag(null, TAG_SHORTCUT);
465 ShortcutService.writeAttr(out, ATTR_ID, si.getId());
466 // writeAttr(out, "package", si.getPackageName()); // not needed
467 ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
468 // writeAttr(out, "icon", si.getIcon()); // We don't save it.
469 ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700470 ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
Makoto Onuki31459242016-03-22 11:12:18 -0700471 ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
472 ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
473 ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
474 si.getLastChangedTimestamp());
Makoto Onuki0acbb142016-03-22 17:02:57 -0700475 if (forBackup) {
476 // Don't write icon information. Also drop the dynamic flag.
477 ShortcutService.writeAttr(out, ATTR_FLAGS,
478 si.getFlags() &
479 ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
480 | ShortcutInfo.FLAG_DYNAMIC));
481 } else {
482 ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
483 ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
484 ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
485 }
Makoto Onuki31459242016-03-22 11:12:18 -0700486
487 ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
488 si.getIntentPersistableExtras());
489 ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
490
491 out.endTag(null, TAG_SHORTCUT);
492 }
493
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700494 public static ShortcutPackage loadFromXml(ShortcutService s, XmlPullParser parser,
495 int ownerUserId, boolean fromBackup)
Makoto Onuki31459242016-03-22 11:12:18 -0700496 throws IOException, XmlPullParserException {
497
498 final String packageName = ShortcutService.parseStringAttribute(parser,
499 ATTR_NAME);
500
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700501 final ShortcutPackage ret = new ShortcutPackage(ownerUserId, packageName);
Makoto Onuki31459242016-03-22 11:12:18 -0700502
503 ret.mDynamicShortcutCount =
504 ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
505 ret.mApiCallCount =
506 ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
507 ret.mLastResetTime =
508 ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
509
510 final int outerDepth = parser.getDepth();
511 int type;
512 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
513 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
514 if (type != XmlPullParser.START_TAG) {
515 continue;
516 }
517 final int depth = parser.getDepth();
518 final String tag = parser.getName();
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700519 if (depth == outerDepth + 1) {
520 switch (tag) {
521 case ShortcutPackageInfo.TAG_ROOT:
Makoto Onuki2e210c42016-03-30 08:30:36 -0700522 ret.getPackageInfo().loadFromXml(parser, fromBackup);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700523 continue;
524 case TAG_SHORTCUT:
525 final ShortcutInfo si = parseShortcut(parser, packageName);
Makoto Onuki31459242016-03-22 11:12:18 -0700526
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700527 // Don't use addShortcut(), we don't need to save the icon.
528 ret.mShortcuts.put(si.getId(), si);
529 continue;
530 }
Makoto Onuki31459242016-03-22 11:12:18 -0700531 }
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700532 ShortcutService.warnForInvalidTag(depth, tag);
533 }
Makoto Onuki31459242016-03-22 11:12:18 -0700534 return ret;
535 }
536
537 private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
538 throws IOException, XmlPullParserException {
539 String id;
540 ComponentName activityComponent;
541 // Icon icon;
542 String title;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700543 String text;
Makoto Onuki31459242016-03-22 11:12:18 -0700544 Intent intent;
545 PersistableBundle intentPersistableExtras = null;
546 int weight;
547 PersistableBundle extras = null;
548 long lastChangedTimestamp;
549 int flags;
550 int iconRes;
551 String bitmapPath;
552
553 id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
554 activityComponent = ShortcutService.parseComponentNameAttribute(parser,
555 ATTR_ACTIVITY);
556 title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700557 text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
Makoto Onuki31459242016-03-22 11:12:18 -0700558 intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
559 weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
Makoto Onuki9da23fc2016-03-29 11:14:42 -0700560 lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
Makoto Onuki31459242016-03-22 11:12:18 -0700561 flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
562 iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
563 bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
564
565 final int outerDepth = parser.getDepth();
566 int type;
567 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
568 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
569 if (type != XmlPullParser.START_TAG) {
570 continue;
571 }
572 final int depth = parser.getDepth();
573 final String tag = parser.getName();
574 if (ShortcutService.DEBUG_LOAD) {
575 Slog.d(TAG, String.format(" depth=%d type=%d name=%s",
576 depth, type, tag));
577 }
578 switch (tag) {
579 case TAG_INTENT_EXTRAS:
580 intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
581 continue;
582 case TAG_EXTRAS:
583 extras = PersistableBundle.restoreFromXml(parser);
584 continue;
585 }
586 throw ShortcutService.throwForInvalidTag(depth, tag);
587 }
588 return new ShortcutInfo(
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700589 id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
Makoto Onuki31459242016-03-22 11:12:18 -0700590 intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
591 iconRes, bitmapPath);
592 }
Makoto Onuki2e210c42016-03-30 08:30:36 -0700593
594 @VisibleForTesting
595 List<ShortcutInfo> getAllShortcutsForTest() {
596 return new ArrayList<>(mShortcuts.values());
597 }
Makoto Onuki31459242016-03-22 11:12:18 -0700598}