ShortcutService.java revision f34c308dfd9cdda428b8ead8bf655de1608501a9
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 */ 16package com.android.server.pm; 17 18import android.annotation.IntDef; 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.annotation.UserIdInt; 22import android.app.ActivityManager; 23import android.app.ActivityManagerNative; 24import android.app.AppGlobals; 25import android.app.IUidObserver; 26import android.app.usage.UsageStatsManagerInternal; 27import android.content.ComponentName; 28import android.content.Context; 29import android.content.Intent; 30import android.content.pm.ActivityInfo; 31import android.content.pm.ApplicationInfo; 32import android.content.pm.IPackageManager; 33import android.content.pm.IShortcutService; 34import android.content.pm.LauncherApps; 35import android.content.pm.LauncherApps.ShortcutQuery; 36import android.content.pm.PackageInfo; 37import android.content.pm.PackageManager; 38import android.content.pm.PackageManager.NameNotFoundException; 39import android.content.pm.PackageManagerInternal; 40import android.content.pm.ParceledListSlice; 41import android.content.pm.ResolveInfo; 42import android.content.pm.ShortcutInfo; 43import android.content.pm.ShortcutServiceInternal; 44import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 45import android.content.res.Resources; 46import android.content.res.XmlResourceParser; 47import android.graphics.Bitmap; 48import android.graphics.Bitmap.CompressFormat; 49import android.graphics.Canvas; 50import android.graphics.RectF; 51import android.graphics.drawable.Icon; 52import android.os.Binder; 53import android.os.Environment; 54import android.os.FileUtils; 55import android.os.Handler; 56import android.os.Looper; 57import android.os.ParcelFileDescriptor; 58import android.os.PersistableBundle; 59import android.os.Process; 60import android.os.RemoteException; 61import android.os.ResultReceiver; 62import android.os.SELinux; 63import android.os.ServiceManager; 64import android.os.ShellCommand; 65import android.os.SystemClock; 66import android.os.UserHandle; 67import android.os.UserManager; 68import android.text.TextUtils; 69import android.text.format.Time; 70import android.util.ArraySet; 71import android.util.AtomicFile; 72import android.util.KeyValueListParser; 73import android.util.Log; 74import android.util.Slog; 75import android.util.SparseArray; 76import android.util.SparseIntArray; 77import android.util.SparseLongArray; 78import android.util.TypedValue; 79import android.util.Xml; 80import android.view.IWindowManager; 81 82import com.android.internal.annotations.GuardedBy; 83import com.android.internal.annotations.VisibleForTesting; 84import com.android.internal.content.PackageMonitor; 85import com.android.internal.os.BackgroundThread; 86import com.android.internal.util.FastXmlSerializer; 87import com.android.internal.util.Preconditions; 88import com.android.server.LocalServices; 89import com.android.server.SystemService; 90import com.android.server.pm.ShortcutUser.PackageWithUser; 91 92import libcore.io.IoUtils; 93 94import org.xmlpull.v1.XmlPullParser; 95import org.xmlpull.v1.XmlPullParserException; 96import org.xmlpull.v1.XmlSerializer; 97 98import java.io.BufferedInputStream; 99import java.io.BufferedOutputStream; 100import java.io.ByteArrayInputStream; 101import java.io.ByteArrayOutputStream; 102import java.io.File; 103import java.io.FileDescriptor; 104import java.io.FileInputStream; 105import java.io.FileNotFoundException; 106import java.io.FileOutputStream; 107import java.io.IOException; 108import java.io.InputStream; 109import java.io.OutputStream; 110import java.io.PrintWriter; 111import java.lang.annotation.Retention; 112import java.lang.annotation.RetentionPolicy; 113import java.net.URISyntaxException; 114import java.nio.charset.StandardCharsets; 115import java.util.ArrayList; 116import java.util.Collections; 117import java.util.List; 118import java.util.concurrent.atomic.AtomicBoolean; 119import java.util.concurrent.atomic.AtomicLong; 120import java.util.function.Consumer; 121import java.util.function.Predicate; 122 123/** 124 * TODO: 125 * - Deal with the async nature of PACKAGE_ADD. Basically when a publisher does anything after 126 * it's upgraded, the manager should make sure the upgrade process has been executed. 127 * 128 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi. 129 * -> But TypedValue.applyDimension() doesn't differentiate x and y..? 130 * 131 * - Default launcher check does take a few ms. Worth caching. 132 * 133 * - Detect when already registered instances are passed to APIs again, which might break 134 * internal bitmap handling. 135 * 136 * - Add more call stats. 137 */ 138public class ShortcutService extends IShortcutService.Stub { 139 static final String TAG = "ShortcutService"; 140 141 static final boolean DEBUG = false; // STOPSHIP if true 142 static final boolean DEBUG_LOAD = false; // STOPSHIP if true 143 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true 144 145 @VisibleForTesting 146 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day 147 148 @VisibleForTesting 149 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10; 150 151 @VisibleForTesting 152 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5; 153 154 @VisibleForTesting 155 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; 156 157 @VisibleForTesting 158 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48; 159 160 @VisibleForTesting 161 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name(); 162 163 @VisibleForTesting 164 static final int DEFAULT_ICON_PERSIST_QUALITY = 100; 165 166 @VisibleForTesting 167 static final int DEFAULT_SAVE_DELAY_MS = 3000; 168 169 @VisibleForTesting 170 static final String FILENAME_BASE_STATE = "shortcut_service.xml"; 171 172 @VisibleForTesting 173 static final String DIRECTORY_PER_USER = "shortcut_service"; 174 175 @VisibleForTesting 176 static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; 177 178 static final String DIRECTORY_BITMAPS = "bitmaps"; 179 180 private static final String TAG_ROOT = "root"; 181 private static final String TAG_LAST_RESET_TIME = "last_reset_time"; 182 private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no"; 183 184 private static final String ATTR_VALUE = "value"; 185 186 private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER; 187 188 @VisibleForTesting 189 interface ConfigConstants { 190 /** 191 * Key name for the save delay, in milliseconds. (int) 192 */ 193 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms"; 194 195 /** 196 * Key name for the throttling reset interval, in seconds. (long) 197 */ 198 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec"; 199 200 /** 201 * Key name for the max number of modifying API calls per app for every interval. (int) 202 */ 203 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval"; 204 205 /** 206 * Key name for the max icon dimensions in DP, for non-low-memory devices. 207 */ 208 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp"; 209 210 /** 211 * Key name for the max icon dimensions in DP, for low-memory devices. 212 */ 213 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram"; 214 215 /** 216 * Key name for the max dynamic shortcuts per activity. (int) 217 */ 218 String KEY_MAX_SHORTCUTS = "max_shortcuts"; 219 220 /** 221 * Key name for icon compression quality, 0-100. 222 */ 223 String KEY_ICON_QUALITY = "icon_quality"; 224 225 /** 226 * Key name for icon compression format: "PNG", "JPEG" or "WEBP" 227 */ 228 String KEY_ICON_FORMAT = "icon_format"; 229 } 230 231 final Context mContext; 232 233 private final Object mLock = new Object(); 234 235 private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0); 236 237 private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = 238 ri -> !ri.activityInfo.exported; 239 240 private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = pi -> !isInstalled(pi); 241 242 private final Handler mHandler; 243 244 @GuardedBy("mLock") 245 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); 246 247 @GuardedBy("mLock") 248 private long mRawLastResetTime; 249 250 /** 251 * User ID -> UserShortcuts 252 */ 253 @GuardedBy("mLock") 254 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>(); 255 256 /** 257 * Max number of dynamic + manifest shortcuts that each application can have at a time. 258 */ 259 private int mMaxShortcuts; 260 261 /** 262 * Max number of updating API calls that each application can make during the interval. 263 */ 264 int mMaxUpdatesPerInterval; 265 266 /** 267 * Actual throttling-reset interval. By default it's a day. 268 */ 269 private long mResetInterval; 270 271 /** 272 * Icon max width/height in pixels. 273 */ 274 private int mMaxIconDimension; 275 276 private CompressFormat mIconPersistFormat; 277 private int mIconPersistQuality; 278 279 private int mSaveDelayMillis; 280 281 private final IPackageManager mIPackageManager; 282 private final PackageManagerInternal mPackageManagerInternal; 283 private final UserManager mUserManager; 284 private final UsageStatsManagerInternal mUsageStatsManagerInternal; 285 286 @GuardedBy("mLock") 287 final SparseIntArray mUidState = new SparseIntArray(); 288 289 @GuardedBy("mLock") 290 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray(); 291 292 @GuardedBy("mLock") 293 private List<Integer> mDirtyUserIds = new ArrayList<>(); 294 295 /** 296 * A counter that increments every time the system locale changes. We keep track of it to 297 * reset 298 * throttling counters on the first call from each package after the last locale change. 299 * 300 * We need this mechanism because we can't do much in the locale change callback, which is 301 * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}. 302 */ 303 private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong(); 304 305 private final AtomicBoolean mBootCompleted = new AtomicBoolean(); 306 307 private static final int PACKAGE_MATCH_FLAGS = 308 PackageManager.MATCH_DIRECT_BOOT_AWARE 309 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 310 | PackageManager.MATCH_UNINSTALLED_PACKAGES; 311 312 // Stats 313 @VisibleForTesting 314 interface Stats { 315 int GET_DEFAULT_HOME = 0; 316 int GET_PACKAGE_INFO = 1; 317 int GET_PACKAGE_INFO_WITH_SIG = 2; 318 int GET_APPLICATION_INFO = 3; 319 int LAUNCHER_PERMISSION_CHECK = 4; 320 int CLEANUP_DANGLING_BITMAPS = 5; 321 int GET_ACTIVITY_WITH_METADATA = 6; 322 int GET_INSTALLED_PACKAGES = 7; 323 int CHECK_PACKAGE_CHANGES = 8; 324 int GET_APPLICATION_RESOURCES = 9; 325 int RESOURCE_NAME_LOOKUP = 10; 326 int GET_LAUNCHER_ACTIVITY = 11; 327 int CHECK_LAUNCHER_ACTIVITY = 12; 328 int IS_ACTIVITY_ENABLED = 13; 329 330 int COUNT = IS_ACTIVITY_ENABLED + 1; 331 } 332 333 final Object mStatLock = new Object(); 334 335 @GuardedBy("mStatLock") 336 private final int[] mCountStats = new int[Stats.COUNT]; 337 338 @GuardedBy("mStatLock") 339 private final long[] mDurationStats = new long[Stats.COUNT]; 340 341 private static final int PROCESS_STATE_FOREGROUND_THRESHOLD = 342 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; 343 344 static final int OPERATION_SET = 0; 345 static final int OPERATION_ADD = 1; 346 static final int OPERATION_UPDATE = 2; 347 348 /** @hide */ 349 @IntDef(value = { 350 OPERATION_SET, 351 OPERATION_ADD, 352 OPERATION_UPDATE 353 }) 354 @Retention(RetentionPolicy.SOURCE) 355 @interface ShortcutOperation { 356 } 357 358 @GuardedBy("mLock") 359 private int mWtfCount = 0; 360 361 @GuardedBy("mLock") 362 private Exception mLastWtfStacktrace; 363 364 public ShortcutService(Context context) { 365 this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); 366 } 367 368 @VisibleForTesting 369 ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) { 370 mContext = Preconditions.checkNotNull(context); 371 LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); 372 mHandler = new Handler(looper); 373 mIPackageManager = AppGlobals.getPackageManager(); 374 mPackageManagerInternal = Preconditions.checkNotNull( 375 LocalServices.getService(PackageManagerInternal.class)); 376 mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class)); 377 mUsageStatsManagerInternal = Preconditions.checkNotNull( 378 LocalServices.getService(UsageStatsManagerInternal.class)); 379 380 if (onlyForPackageManagerApis) { 381 return; // Don't do anything further. For unit tests only. 382 } 383 384 mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false); 385 386 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE 387 | ActivityManager.UID_OBSERVER_GONE); 388 } 389 390 void logDurationStat(int statId, long start) { 391 synchronized (mStatLock) { 392 mCountStats[statId]++; 393 mDurationStats[statId] += (injectElapsedRealtime() - start); 394 } 395 } 396 397 public long getLocaleChangeSequenceNumber() { 398 return mLocaleChangeSequenceNumber.get(); 399 } 400 401 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 402 @Override 403 public void onUidStateChanged(int uid, int procState) throws RemoteException { 404 handleOnUidStateChanged(uid, procState); 405 } 406 407 @Override 408 public void onUidGone(int uid) throws RemoteException { 409 handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE); 410 } 411 412 @Override 413 public void onUidActive(int uid) throws RemoteException { 414 } 415 416 @Override 417 public void onUidIdle(int uid) throws RemoteException { 418 } 419 }; 420 421 void handleOnUidStateChanged(int uid, int procState) { 422 if (DEBUG_PROCSTATE) { 423 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState); 424 } 425 synchronized (mLock) { 426 mUidState.put(uid, procState); 427 428 // We need to keep track of last time an app comes to foreground. 429 // See ShortcutPackage.getApiCallCount() for how it's used. 430 // It doesn't have to be persisted, but it needs to be the elapsed time. 431 if (isProcessStateForeground(procState)) { 432 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime()); 433 } 434 } 435 } 436 437 private boolean isProcessStateForeground(int processState) { 438 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD; 439 } 440 441 boolean isUidForegroundLocked(int uid) { 442 if (uid == Process.SYSTEM_UID) { 443 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services, 444 // so it's foreground anyway. 445 return true; 446 } 447 return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE)); 448 } 449 450 long getUidLastForegroundElapsedTimeLocked(int uid) { 451 return mUidLastForegroundElapsedTime.get(uid); 452 } 453 454 /** 455 * System service lifecycle. 456 */ 457 public static final class Lifecycle extends SystemService { 458 final ShortcutService mService; 459 460 public Lifecycle(Context context) { 461 super(context); 462 mService = new ShortcutService(context); 463 } 464 465 @Override 466 public void onStart() { 467 publishBinderService(Context.SHORTCUT_SERVICE, mService); 468 } 469 470 @Override 471 public void onBootPhase(int phase) { 472 mService.onBootPhase(phase); 473 } 474 475 @Override 476 public void onCleanupUser(int userHandle) { 477 mService.handleCleanupUser(userHandle); 478 } 479 480 @Override 481 public void onUnlockUser(int userId) { 482 mService.handleUnlockUser(userId); 483 } 484 } 485 486 /** lifecycle event */ 487 void onBootPhase(int phase) { 488 if (DEBUG) { 489 Slog.d(TAG, "onBootPhase: " + phase); 490 } 491 switch (phase) { 492 case SystemService.PHASE_LOCK_SETTINGS_READY: 493 initialize(); 494 break; 495 case SystemService.PHASE_BOOT_COMPLETED: 496 mBootCompleted.set(true); 497 break; 498 } 499 } 500 501 /** lifecycle event */ 502 void handleUnlockUser(int userId) { 503 if (DEBUG) { 504 Slog.d(TAG, "handleUnlockUser: user=" + userId); 505 } 506 synchronized (mLock) { 507 // Preload 508 getUserShortcutsLocked(userId); 509 510 checkPackageChanges(userId); 511 } 512 } 513 514 /** lifecycle event */ 515 void handleCleanupUser(int userId) { 516 synchronized (mLock) { 517 unloadUserLocked(userId); 518 } 519 } 520 521 private void unloadUserLocked(int userId) { 522 if (DEBUG) { 523 Slog.d(TAG, "unloadUserLocked: user=" + userId); 524 } 525 // Save all dirty information. 526 saveDirtyInfo(); 527 528 // Unload 529 mUsers.delete(userId); 530 } 531 532 /** Return the base state file name */ 533 private AtomicFile getBaseStateFile() { 534 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE); 535 path.mkdirs(); 536 return new AtomicFile(path); 537 } 538 539 /** 540 * Init the instance. (load the state file, etc) 541 */ 542 private void initialize() { 543 synchronized (mLock) { 544 loadConfigurationLocked(); 545 loadBaseStateLocked(); 546 } 547 } 548 549 /** 550 * Load the configuration from Settings. 551 */ 552 private void loadConfigurationLocked() { 553 updateConfigurationLocked(injectShortcutManagerConstants()); 554 } 555 556 /** 557 * Load the configuration from Settings. 558 */ 559 @VisibleForTesting 560 boolean updateConfigurationLocked(String config) { 561 boolean result = true; 562 563 final KeyValueListParser parser = new KeyValueListParser(','); 564 try { 565 parser.setString(config); 566 } catch (IllegalArgumentException e) { 567 // Failed to parse the settings string, log this and move on 568 // with defaults. 569 Slog.e(TAG, "Bad shortcut manager settings", e); 570 result = false; 571 } 572 573 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS, 574 DEFAULT_SAVE_DELAY_MS)); 575 576 mResetInterval = Math.max(1, parser.getLong( 577 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC) 578 * 1000L); 579 580 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong( 581 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL)); 582 583 mMaxShortcuts = Math.max(0, (int) parser.getLong( 584 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP)); 585 586 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() 587 ? (int) parser.getLong( 588 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, 589 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP) 590 : (int) parser.getLong( 591 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP, 592 DEFAULT_MAX_ICON_DIMENSION_DP)); 593 594 mMaxIconDimension = injectDipToPixel(iconDimensionDp); 595 596 mIconPersistFormat = CompressFormat.valueOf( 597 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT)); 598 599 mIconPersistQuality = (int) parser.getLong( 600 ConfigConstants.KEY_ICON_QUALITY, 601 DEFAULT_ICON_PERSIST_QUALITY); 602 603 return result; 604 } 605 606 @VisibleForTesting 607 String injectShortcutManagerConstants() { 608 return android.provider.Settings.Global.getString( 609 mContext.getContentResolver(), 610 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS); 611 } 612 613 @VisibleForTesting 614 int injectDipToPixel(int dip) { 615 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 616 mContext.getResources().getDisplayMetrics()); 617 } 618 619 // === Persisting === 620 621 @Nullable 622 static String parseStringAttribute(XmlPullParser parser, String attribute) { 623 return parser.getAttributeValue(null, attribute); 624 } 625 626 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) { 627 return parseLongAttribute(parser, attribute) == 1; 628 } 629 630 static int parseIntAttribute(XmlPullParser parser, String attribute) { 631 return (int) parseLongAttribute(parser, attribute); 632 } 633 634 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) { 635 return (int) parseLongAttribute(parser, attribute, def); 636 } 637 638 static long parseLongAttribute(XmlPullParser parser, String attribute) { 639 return parseLongAttribute(parser, attribute, 0); 640 } 641 642 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) { 643 final String value = parseStringAttribute(parser, attribute); 644 if (TextUtils.isEmpty(value)) { 645 return def; 646 } 647 try { 648 return Long.parseLong(value); 649 } catch (NumberFormatException e) { 650 Slog.e(TAG, "Error parsing long " + value); 651 return def; 652 } 653 } 654 655 @Nullable 656 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { 657 final String value = parseStringAttribute(parser, attribute); 658 if (TextUtils.isEmpty(value)) { 659 return null; 660 } 661 return ComponentName.unflattenFromString(value); 662 } 663 664 @Nullable 665 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { 666 final String value = parseStringAttribute(parser, attribute); 667 Intent parsed = null; 668 if (!TextUtils.isEmpty(value)) { 669 try { 670 parsed = Intent.parseUri(value, /* flags =*/ 0); 671 } catch (URISyntaxException e) { 672 Slog.e(TAG, "Error parsing intent", e); 673 } 674 } 675 if (parsed == null) { 676 // Default intent. 677 parsed = new Intent(Intent.ACTION_VIEW); 678 } 679 return parsed; 680 } 681 682 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { 683 if (TextUtils.isEmpty(value)) return; 684 685 out.startTag(null, tag); 686 out.attribute(null, ATTR_VALUE, value); 687 out.endTag(null, tag); 688 } 689 690 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { 691 writeTagValue(out, tag, Long.toString(value)); 692 } 693 694 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { 695 if (name == null) return; 696 writeTagValue(out, tag, name.flattenToString()); 697 } 698 699 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) 700 throws IOException, XmlPullParserException { 701 if (bundle == null) return; 702 703 out.startTag(null, tag); 704 bundle.saveToXml(out); 705 out.endTag(null, tag); 706 } 707 708 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException { 709 if (TextUtils.isEmpty(value)) return; 710 711 out.attribute(null, name, value.toString()); 712 } 713 714 static void writeAttr(XmlSerializer out, String name, long value) throws IOException { 715 writeAttr(out, name, String.valueOf(value)); 716 } 717 718 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException { 719 if (value) { 720 writeAttr(out, name, "1"); 721 } 722 } 723 724 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { 725 if (comp == null) return; 726 writeAttr(out, name, comp.flattenToString()); 727 } 728 729 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { 730 if (intent == null) return; 731 732 writeAttr(out, name, intent.toUri(/* flags =*/ 0)); 733 } 734 735 @VisibleForTesting 736 void saveBaseStateLocked() { 737 final AtomicFile file = getBaseStateFile(); 738 if (DEBUG) { 739 Slog.d(TAG, "Saving to " + file.getBaseFile()); 740 } 741 742 FileOutputStream outs = null; 743 try { 744 outs = file.startWrite(); 745 746 // Write to XML 747 XmlSerializer out = new FastXmlSerializer(); 748 out.setOutput(outs, StandardCharsets.UTF_8.name()); 749 out.startDocument(null, true); 750 out.startTag(null, TAG_ROOT); 751 752 // Body. 753 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime); 754 writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER, 755 mLocaleChangeSequenceNumber.get()); 756 757 // Epilogue. 758 out.endTag(null, TAG_ROOT); 759 out.endDocument(); 760 761 // Close. 762 file.finishWrite(outs); 763 } catch (IOException e) { 764 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 765 file.failWrite(outs); 766 } 767 } 768 769 private void loadBaseStateLocked() { 770 mRawLastResetTime = 0; 771 772 final AtomicFile file = getBaseStateFile(); 773 if (DEBUG) { 774 Slog.d(TAG, "Loading from " + file.getBaseFile()); 775 } 776 try (FileInputStream in = file.openRead()) { 777 XmlPullParser parser = Xml.newPullParser(); 778 parser.setInput(in, StandardCharsets.UTF_8.name()); 779 780 int type; 781 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 782 if (type != XmlPullParser.START_TAG) { 783 continue; 784 } 785 final int depth = parser.getDepth(); 786 // Check the root tag 787 final String tag = parser.getName(); 788 if (depth == 1) { 789 if (!TAG_ROOT.equals(tag)) { 790 Slog.e(TAG, "Invalid root tag: " + tag); 791 return; 792 } 793 continue; 794 } 795 // Assume depth == 2 796 switch (tag) { 797 case TAG_LAST_RESET_TIME: 798 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE); 799 break; 800 case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER: 801 mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE)); 802 break; 803 default: 804 Slog.e(TAG, "Invalid tag: " + tag); 805 break; 806 } 807 } 808 } catch (FileNotFoundException e) { 809 // Use the default 810 } catch (IOException | XmlPullParserException e) { 811 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 812 813 mRawLastResetTime = 0; 814 } 815 // Adjust the last reset time. 816 getLastResetTimeLocked(); 817 } 818 819 private void saveUserLocked(@UserIdInt int userId) { 820 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 821 if (DEBUG) { 822 Slog.d(TAG, "Saving to " + path); 823 } 824 path.mkdirs(); 825 final AtomicFile file = new AtomicFile(path); 826 FileOutputStream os = null; 827 try { 828 os = file.startWrite(); 829 830 saveUserInternalLocked(userId, os, /* forBackup= */ false); 831 832 file.finishWrite(os); 833 } catch (XmlPullParserException | IOException e) { 834 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 835 file.failWrite(os); 836 } 837 } 838 839 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, 840 boolean forBackup) throws IOException, XmlPullParserException { 841 842 final BufferedOutputStream bos = new BufferedOutputStream(os); 843 844 // Write to XML 845 XmlSerializer out = new FastXmlSerializer(); 846 out.setOutput(bos, StandardCharsets.UTF_8.name()); 847 out.startDocument(null, true); 848 849 getUserShortcutsLocked(userId).saveToXml(out, forBackup); 850 851 out.endDocument(); 852 853 bos.flush(); 854 os.flush(); 855 } 856 857 static IOException throwForInvalidTag(int depth, String tag) throws IOException { 858 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth)); 859 } 860 861 static void warnForInvalidTag(int depth, String tag) throws IOException { 862 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); 863 } 864 865 @Nullable 866 private ShortcutUser loadUserLocked(@UserIdInt int userId) { 867 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 868 if (DEBUG) { 869 Slog.d(TAG, "Loading from " + path); 870 } 871 final AtomicFile file = new AtomicFile(path); 872 873 final FileInputStream in; 874 try { 875 in = file.openRead(); 876 } catch (FileNotFoundException e) { 877 if (DEBUG) { 878 Slog.d(TAG, "Not found " + path); 879 } 880 return null; 881 } 882 try { 883 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false); 884 cleanupDanglingBitmapDirectoriesLocked(userId, ret); 885 return ret; 886 } catch (IOException | XmlPullParserException e) { 887 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 888 return null; 889 } finally { 890 IoUtils.closeQuietly(in); 891 } 892 } 893 894 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, 895 boolean fromBackup) throws XmlPullParserException, IOException { 896 897 final BufferedInputStream bis = new BufferedInputStream(is); 898 899 ShortcutUser ret = null; 900 XmlPullParser parser = Xml.newPullParser(); 901 parser.setInput(bis, StandardCharsets.UTF_8.name()); 902 903 int type; 904 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 905 if (type != XmlPullParser.START_TAG) { 906 continue; 907 } 908 final int depth = parser.getDepth(); 909 910 final String tag = parser.getName(); 911 if (DEBUG_LOAD) { 912 Slog.d(TAG, String.format("depth=%d type=%d name=%s", 913 depth, type, tag)); 914 } 915 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) { 916 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup); 917 continue; 918 } 919 throwForInvalidTag(depth, tag); 920 } 921 return ret; 922 } 923 924 private void scheduleSaveBaseState() { 925 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. 926 } 927 928 void scheduleSaveUser(@UserIdInt int userId) { 929 scheduleSaveInner(userId); 930 } 931 932 // In order to re-schedule, we need to reuse the same instance, so keep it in final. 933 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo; 934 935 private void scheduleSaveInner(@UserIdInt int userId) { 936 if (DEBUG) { 937 Slog.d(TAG, "Scheduling to save for " + userId); 938 } 939 synchronized (mLock) { 940 if (!mDirtyUserIds.contains(userId)) { 941 mDirtyUserIds.add(userId); 942 } 943 } 944 // If already scheduled, remove that and re-schedule in N seconds. 945 mHandler.removeCallbacks(mSaveDirtyInfoRunner); 946 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis); 947 } 948 949 @VisibleForTesting 950 void saveDirtyInfo() { 951 if (DEBUG) { 952 Slog.d(TAG, "saveDirtyInfo"); 953 } 954 synchronized (mLock) { 955 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) { 956 final int userId = mDirtyUserIds.get(i); 957 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. 958 saveBaseStateLocked(); 959 } else { 960 saveUserLocked(userId); 961 } 962 } 963 mDirtyUserIds.clear(); 964 } 965 } 966 967 /** Return the last reset time. */ 968 long getLastResetTimeLocked() { 969 updateTimesLocked(); 970 return mRawLastResetTime; 971 } 972 973 /** Return the next reset time. */ 974 long getNextResetTimeLocked() { 975 updateTimesLocked(); 976 return mRawLastResetTime + mResetInterval; 977 } 978 979 static boolean isClockValid(long time) { 980 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT 981 } 982 983 /** 984 * Update the last reset time. 985 */ 986 private void updateTimesLocked() { 987 988 final long now = injectCurrentTimeMillis(); 989 990 final long prevLastResetTime = mRawLastResetTime; 991 992 if (mRawLastResetTime == 0) { // first launch. 993 // TODO Randomize?? 994 mRawLastResetTime = now; 995 } else if (now < mRawLastResetTime) { 996 // Clock rewound. 997 if (isClockValid(now)) { 998 Slog.w(TAG, "Clock rewound"); 999 // TODO Randomize?? 1000 mRawLastResetTime = now; 1001 } 1002 } else { 1003 if ((mRawLastResetTime + mResetInterval) <= now) { 1004 final long offset = mRawLastResetTime % mResetInterval; 1005 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset; 1006 } 1007 } 1008 if (prevLastResetTime != mRawLastResetTime) { 1009 scheduleSaveBaseState(); 1010 } 1011 } 1012 1013 @GuardedBy("mLock") 1014 @NonNull 1015 private boolean isUserLoadedLocked(@UserIdInt int userId) { 1016 return mUsers.get(userId) != null; 1017 } 1018 1019 /** Return the per-user state. */ 1020 @GuardedBy("mLock") 1021 @NonNull 1022 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { 1023 ShortcutUser userPackages = mUsers.get(userId); 1024 if (userPackages == null) { 1025 userPackages = loadUserLocked(userId); 1026 if (userPackages == null) { 1027 userPackages = new ShortcutUser(this, userId); 1028 } 1029 mUsers.put(userId, userPackages); 1030 } 1031 return userPackages; 1032 } 1033 1034 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) { 1035 for (int i = mUsers.size() - 1; i >= 0; i--) { 1036 c.accept(mUsers.valueAt(i)); 1037 } 1038 } 1039 1040 /** Return the per-user per-package state. */ 1041 @GuardedBy("mLock") 1042 @NonNull 1043 ShortcutPackage getPackageShortcutsLocked( 1044 @NonNull String packageName, @UserIdInt int userId) { 1045 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName); 1046 } 1047 1048 @GuardedBy("mLock") 1049 @NonNull 1050 ShortcutLauncher getLauncherShortcutsLocked( 1051 @NonNull String packageName, @UserIdInt int ownerUserId, 1052 @UserIdInt int launcherUserId) { 1053 return getUserShortcutsLocked(ownerUserId) 1054 .getLauncherShortcuts(packageName, launcherUserId); 1055 } 1056 1057 // === Caller validation === 1058 1059 void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) { 1060 if (shortcut.getBitmapPath() != null) { 1061 if (DEBUG) { 1062 Slog.d(TAG, "Removing " + shortcut.getBitmapPath()); 1063 } 1064 new File(shortcut.getBitmapPath()).delete(); 1065 1066 shortcut.setBitmapPath(null); 1067 } 1068 shortcut.setIconResourceId(0); 1069 shortcut.setIconResName(null); 1070 shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES); 1071 } 1072 1073 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) { 1074 final File packagePath = new File(getUserBitmapFilePath(userId), packageName); 1075 if (!packagePath.isDirectory()) { 1076 return; 1077 } 1078 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) { 1079 Slog.w(TAG, "Unable to remove directory " + packagePath); 1080 } 1081 } 1082 1083 private void cleanupDanglingBitmapDirectoriesLocked( 1084 @UserIdInt int userId, @NonNull ShortcutUser user) { 1085 if (DEBUG) { 1086 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); 1087 } 1088 final long start = injectElapsedRealtime(); 1089 1090 final File bitmapDir = getUserBitmapFilePath(userId); 1091 final File[] children = bitmapDir.listFiles(); 1092 if (children == null) { 1093 return; 1094 } 1095 for (File child : children) { 1096 if (!child.isDirectory()) { 1097 continue; 1098 } 1099 final String packageName = child.getName(); 1100 if (DEBUG) { 1101 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName); 1102 } 1103 if (!user.hasPackage(packageName)) { 1104 if (DEBUG) { 1105 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName); 1106 } 1107 cleanupBitmapsForPackage(userId, packageName); 1108 } else { 1109 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child); 1110 } 1111 } 1112 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start); 1113 } 1114 1115 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user, 1116 @NonNull String packageName, @NonNull File path) { 1117 final ArraySet<String> usedFiles = 1118 user.getPackageShortcuts(packageName).getUsedBitmapFiles(); 1119 1120 for (File child : path.listFiles()) { 1121 if (!child.isFile()) { 1122 continue; 1123 } 1124 final String name = child.getName(); 1125 if (!usedFiles.contains(name)) { 1126 if (DEBUG) { 1127 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath()); 1128 } 1129 child.delete(); 1130 } 1131 } 1132 } 1133 1134 @VisibleForTesting 1135 static class FileOutputStreamWithPath extends FileOutputStream { 1136 private final File mFile; 1137 1138 public FileOutputStreamWithPath(File file) throws FileNotFoundException { 1139 super(file); 1140 mFile = file; 1141 } 1142 1143 public File getFile() { 1144 return mFile; 1145 } 1146 } 1147 1148 /** 1149 * Build the cached bitmap filename for a shortcut icon. 1150 * 1151 * The filename will be based on the ID, except certain characters will be escaped. 1152 */ 1153 @VisibleForTesting 1154 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut) 1155 throws IOException { 1156 final File packagePath = new File(getUserBitmapFilePath(userId), 1157 shortcut.getPackage()); 1158 if (!packagePath.isDirectory()) { 1159 packagePath.mkdirs(); 1160 if (!packagePath.isDirectory()) { 1161 throw new IOException("Unable to create directory " + packagePath); 1162 } 1163 SELinux.restorecon(packagePath); 1164 } 1165 1166 final String baseName = String.valueOf(injectCurrentTimeMillis()); 1167 for (int suffix = 0; ; suffix++) { 1168 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png"; 1169 final File file = new File(packagePath, filename); 1170 if (!file.exists()) { 1171 if (DEBUG) { 1172 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath()); 1173 } 1174 return new FileOutputStreamWithPath(file); 1175 } 1176 } 1177 } 1178 1179 void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) { 1180 if (shortcut.hasIconFile() || shortcut.hasIconResource()) { 1181 return; 1182 } 1183 1184 final long token = injectClearCallingIdentity(); 1185 try { 1186 // Clear icon info on the shortcut. 1187 removeIcon(userId, shortcut); 1188 1189 final Icon icon = shortcut.getIcon(); 1190 if (icon == null) { 1191 return; // has no icon 1192 } 1193 1194 Bitmap bitmap; 1195 try { 1196 switch (icon.getType()) { 1197 case Icon.TYPE_RESOURCE: { 1198 injectValidateIconResPackage(shortcut, icon); 1199 1200 shortcut.setIconResourceId(icon.getResId()); 1201 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES); 1202 return; 1203 } 1204 case Icon.TYPE_BITMAP: { 1205 bitmap = icon.getBitmap(); // Don't recycle in this case. 1206 break; 1207 } 1208 default: 1209 // This shouldn't happen because we've already validated the icon, but 1210 // just in case. 1211 throw ShortcutInfo.getInvalidIconException(); 1212 } 1213 if (bitmap == null) { 1214 Slog.e(TAG, "Null bitmap detected"); 1215 return; 1216 } 1217 // Shrink and write to the file. 1218 File path = null; 1219 try { 1220 final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut); 1221 try { 1222 path = out.getFile(); 1223 1224 Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension); 1225 try { 1226 shrunk.compress(mIconPersistFormat, mIconPersistQuality, out); 1227 } finally { 1228 if (bitmap != shrunk) { 1229 shrunk.recycle(); 1230 } 1231 } 1232 1233 shortcut.setBitmapPath(out.getFile().getAbsolutePath()); 1234 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE); 1235 } finally { 1236 IoUtils.closeQuietly(out); 1237 } 1238 } catch (IOException | RuntimeException e) { 1239 // STOPSHIP Change wtf to e 1240 Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e); 1241 if (path != null && path.exists()) { 1242 path.delete(); 1243 } 1244 } 1245 } finally { 1246 // Once saved, we won't use the original icon information, so null it out. 1247 shortcut.clearIcon(); 1248 } 1249 } finally { 1250 injectRestoreCallingIdentity(token); 1251 } 1252 } 1253 1254 // Unfortunately we can't do this check in unit tests because we fake creator package names, 1255 // so override in unit tests. 1256 // TODO CTS this case. 1257 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) { 1258 if (!shortcut.getPackage().equals(icon.getResPackage())) { 1259 throw new IllegalArgumentException( 1260 "Icon resource must reside in shortcut owner package"); 1261 } 1262 } 1263 1264 @VisibleForTesting 1265 static Bitmap shrinkBitmap(Bitmap in, int maxSize) { 1266 // Original width/height. 1267 final int ow = in.getWidth(); 1268 final int oh = in.getHeight(); 1269 if ((ow <= maxSize) && (oh <= maxSize)) { 1270 if (DEBUG) { 1271 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh)); 1272 } 1273 return in; 1274 } 1275 final int longerDimension = Math.max(ow, oh); 1276 1277 // New width and height. 1278 final int nw = ow * maxSize / longerDimension; 1279 final int nh = oh * maxSize / longerDimension; 1280 if (DEBUG) { 1281 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d", 1282 ow, oh, nw, nh)); 1283 } 1284 1285 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888); 1286 final Canvas c = new Canvas(scaledBitmap); 1287 1288 final RectF dst = new RectF(0, 0, nw, nh); 1289 1290 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null); 1291 1292 return scaledBitmap; 1293 } 1294 1295 /** 1296 * For a shortcut, update all resource names from resource IDs, and also update all 1297 * resource-based strings. 1298 */ 1299 void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) { 1300 final Resources publisherRes = injectGetResourcesForApplicationAsUser( 1301 si.getPackage(), si.getUserId()); 1302 if (publisherRes != null) { 1303 final long start = injectElapsedRealtime(); 1304 try { 1305 si.lookupAndFillInResourceNames(publisherRes); 1306 } finally { 1307 logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start); 1308 } 1309 si.resolveResourceStrings(publisherRes); 1310 } 1311 } 1312 1313 // === Caller validation === 1314 1315 private boolean isCallerSystem() { 1316 final int callingUid = injectBinderCallingUid(); 1317 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID); 1318 } 1319 1320 private boolean isCallerShell() { 1321 final int callingUid = injectBinderCallingUid(); 1322 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 1323 } 1324 1325 private void enforceSystemOrShell() { 1326 Preconditions.checkState(isCallerSystem() || isCallerShell(), 1327 "Caller must be system or shell"); 1328 } 1329 1330 private void enforceShell() { 1331 Preconditions.checkState(isCallerShell(), "Caller must be shell"); 1332 } 1333 1334 private void enforceSystem() { 1335 Preconditions.checkState(isCallerSystem(), "Caller must be system"); 1336 } 1337 1338 private void enforceResetThrottlingPermission() { 1339 if (isCallerSystem()) { 1340 return; 1341 } 1342 injectEnforceCallingPermission( 1343 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null); 1344 } 1345 1346 /** 1347 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse 1348 * mockito. So instead we extracted it here and override it in the tests. 1349 */ 1350 @VisibleForTesting 1351 void injectEnforceCallingPermission( 1352 @NonNull String permission, @Nullable String message) { 1353 mContext.enforceCallingPermission(permission, message); 1354 } 1355 1356 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { 1357 Preconditions.checkStringNotEmpty(packageName, "packageName"); 1358 1359 if (isCallerSystem()) { 1360 return; // no check 1361 } 1362 1363 final int callingUid = injectBinderCallingUid(); 1364 1365 // Otherwise, make sure the arguments are valid. 1366 if (UserHandle.getUserId(callingUid) != userId) { 1367 throw new SecurityException("Invalid user-ID"); 1368 } 1369 if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) { 1370 return; // Caller is valid. 1371 } 1372 throw new SecurityException("Calling package name mismatch"); 1373 } 1374 1375 // Overridden in unit tests to execute r synchronously. 1376 void injectPostToHandler(Runnable r) { 1377 mHandler.post(r); 1378 } 1379 1380 /** 1381 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than 1382 * {@link #getMaxActivityShortcuts()}. 1383 */ 1384 void enforceMaxActivityShortcuts(int numShortcuts) { 1385 if (numShortcuts > mMaxShortcuts) { 1386 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded"); 1387 } 1388 } 1389 1390 /** 1391 * Return the max number of dynamic + manifest shortcuts for each launcher icon. 1392 */ 1393 int getMaxActivityShortcuts() { 1394 return mMaxShortcuts; 1395 } 1396 1397 /** 1398 * - Sends a notification to LauncherApps 1399 * - Write to file 1400 */ 1401 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) { 1402 if (DEBUG) { 1403 Slog.d(TAG, String.format( 1404 "Shortcut changes: package=%s, user=%d", packageName, userId)); 1405 } 1406 notifyListeners(packageName, userId); 1407 scheduleSaveUser(userId); 1408 } 1409 1410 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) { 1411 final long token = injectClearCallingIdentity(); 1412 try { 1413 if (!mUserManager.isUserRunning(userId)) { 1414 return; 1415 } 1416 } finally { 1417 injectRestoreCallingIdentity(token); 1418 } 1419 injectPostToHandler(() -> { 1420 final ArrayList<ShortcutChangeListener> copy; 1421 synchronized (mLock) { 1422 copy = new ArrayList<>(mListeners); 1423 } 1424 // Note onShortcutChanged() needs to be called with the system service permissions. 1425 for (int i = copy.size() - 1; i >= 0; i--) { 1426 copy.get(i).onShortcutChanged(packageName, userId); 1427 } 1428 }); 1429 } 1430 1431 /** 1432 * Clean up / validate an incoming shortcut. 1433 * - Make sure all mandatory fields are set. 1434 * - Make sure the intent's extras are persistable, and them to set 1435 * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras. 1436 * - Clear flags. 1437 * 1438 * TODO Detailed unit tests 1439 */ 1440 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { 1441 Preconditions.checkNotNull(shortcut, "Null shortcut detected"); 1442 if (shortcut.getActivity() != null) { 1443 Preconditions.checkState( 1444 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()), 1445 "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not" 1446 + " belong to package " + shortcut.getPackage()); 1447 } 1448 1449 if (!forUpdate) { 1450 shortcut.enforceMandatoryFields(); 1451 Preconditions.checkArgument( 1452 injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()), 1453 "Cannot publish shortcut: " + shortcut.getActivity() + " is not main activity"); 1454 } 1455 if (shortcut.getIcon() != null) { 1456 ShortcutInfo.validateIcon(shortcut.getIcon()); 1457 } 1458 1459 shortcut.replaceFlags(0); 1460 } 1461 1462 /** 1463 * When a shortcut has no target activity, set the default one from the package. 1464 */ 1465 private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) { 1466 1467 ComponentName defaultActivity = null; 1468 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1469 final ShortcutInfo si = shortcuts.get(i); 1470 if (si.getActivity() == null) { 1471 if (defaultActivity == null) { 1472 defaultActivity = injectGetDefaultMainActivity( 1473 si.getPackage(), si.getUserId()); 1474 Preconditions.checkState(defaultActivity != null, 1475 "Launcher activity not found for package " + si.getPackage()); 1476 } 1477 si.setActivity(defaultActivity); 1478 } 1479 } 1480 } 1481 1482 private void assignImplicitRanks(List<ShortcutInfo> shortcuts) { 1483 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1484 shortcuts.get(i).setImplicitRank(i); 1485 } 1486 } 1487 1488 // === APIs === 1489 1490 @Override 1491 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1492 @UserIdInt int userId) { 1493 verifyCaller(packageName, userId); 1494 1495 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1496 final int size = newShortcuts.size(); 1497 1498 synchronized (mLock) { 1499 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1500 1501 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1502 1503 fillInDefaultActivity(newShortcuts); 1504 1505 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); 1506 1507 // Throttling. 1508 if (!ps.tryApiCall()) { 1509 return false; 1510 } 1511 1512 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1513 ps.clearAllImplicitRanks(); 1514 assignImplicitRanks(newShortcuts); 1515 1516 for (int i = 0; i < size; i++) { 1517 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); 1518 } 1519 1520 // First, remove all un-pinned; dynamic shortcuts 1521 ps.deleteAllDynamicShortcuts(); 1522 1523 // Then, add/update all. We need to make sure to take over "pinned" flag. 1524 for (int i = 0; i < size; i++) { 1525 final ShortcutInfo newShortcut = newShortcuts.get(i); 1526 ps.addOrUpdateDynamicShortcut(newShortcut); 1527 } 1528 1529 // Lastly, adjust the ranks. 1530 ps.adjustRanks(); 1531 } 1532 packageShortcutsChanged(packageName, userId); 1533 1534 verifyStates(); 1535 1536 return true; 1537 } 1538 1539 @Override 1540 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1541 @UserIdInt int userId) { 1542 verifyCaller(packageName, userId); 1543 1544 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1545 final int size = newShortcuts.size(); 1546 1547 synchronized (mLock) { 1548 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1549 1550 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1551 1552 // For update, don't fill in the default activity. Having null activity means 1553 // "don't update the activity" here. 1554 1555 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); 1556 1557 // Throttling. 1558 if (!ps.tryApiCall()) { 1559 return false; 1560 } 1561 1562 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1563 ps.clearAllImplicitRanks(); 1564 assignImplicitRanks(newShortcuts); 1565 1566 for (int i = 0; i < size; i++) { 1567 final ShortcutInfo source = newShortcuts.get(i); 1568 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); 1569 1570 final ShortcutInfo target = ps.findShortcutById(source.getId()); 1571 if (target == null) { 1572 continue; 1573 } 1574 1575 if (target.isEnabled() != source.isEnabled()) { 1576 Slog.w(TAG, 1577 "ShortcutInfo.enabled cannot be changed with updateShortcuts()"); 1578 } 1579 1580 // When updating the rank, we need to insert between existing ranks, so set 1581 // this setRankChanged, and also copy the implicit rank fo adjustRanks(). 1582 if (source.hasRank()) { 1583 target.setRankChanged(); 1584 target.setImplicitRank(source.getImplicitRank()); 1585 } 1586 1587 final boolean replacingIcon = (source.getIcon() != null); 1588 if (replacingIcon) { 1589 removeIcon(userId, target); 1590 } 1591 1592 // Note copyNonNullFieldsFrom() does the "updatable with?" check too. 1593 target.copyNonNullFieldsFrom(source); 1594 target.setTimestamp(injectCurrentTimeMillis()); 1595 1596 if (replacingIcon) { 1597 saveIconAndFixUpShortcut(userId, target); 1598 } 1599 1600 // When we're updating any resource related fields, re-extract the res names and 1601 // the values. 1602 if (replacingIcon || source.hasStringResources()) { 1603 fixUpShortcutResourceNamesAndValues(target); 1604 } 1605 } 1606 1607 // Lastly, adjust the ranks. 1608 ps.adjustRanks(); 1609 } 1610 packageShortcutsChanged(packageName, userId); 1611 1612 verifyStates(); 1613 1614 return true; 1615 } 1616 1617 @Override 1618 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1619 @UserIdInt int userId) { 1620 verifyCaller(packageName, userId); 1621 1622 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1623 final int size = newShortcuts.size(); 1624 1625 synchronized (mLock) { 1626 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1627 1628 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1629 1630 fillInDefaultActivity(newShortcuts); 1631 1632 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); 1633 1634 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1635 ps.clearAllImplicitRanks(); 1636 assignImplicitRanks(newShortcuts); 1637 1638 // Throttling. 1639 if (!ps.tryApiCall()) { 1640 return false; 1641 } 1642 for (int i = 0; i < size; i++) { 1643 final ShortcutInfo newShortcut = newShortcuts.get(i); 1644 1645 // Validate the shortcut. 1646 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); 1647 1648 // When ranks are changing, we need to insert between ranks, so set the 1649 // "rank changed" flag. 1650 newShortcut.setRankChanged(); 1651 1652 // Add it. 1653 ps.addOrUpdateDynamicShortcut(newShortcut); 1654 } 1655 1656 // Lastly, adjust the ranks. 1657 ps.adjustRanks(); 1658 } 1659 packageShortcutsChanged(packageName, userId); 1660 1661 verifyStates(); 1662 1663 return true; 1664 } 1665 1666 @Override 1667 public void disableShortcuts(String packageName, List shortcutIds, 1668 CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { 1669 verifyCaller(packageName, userId); 1670 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1671 1672 synchronized (mLock) { 1673 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1674 1675 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1676 1677 final String disabledMessageString = 1678 (disabledMessage == null) ? null : disabledMessage.toString(); 1679 1680 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1681 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)), 1682 disabledMessageString, disabledMessageResId, 1683 /* overrideImmutable=*/ false); 1684 } 1685 1686 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. 1687 ps.adjustRanks(); 1688 } 1689 packageShortcutsChanged(packageName, userId); 1690 1691 verifyStates(); 1692 } 1693 1694 @Override 1695 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { 1696 verifyCaller(packageName, userId); 1697 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1698 1699 synchronized (mLock) { 1700 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1701 1702 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1703 1704 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1705 ps.enableWithId((String) shortcutIds.get(i)); 1706 } 1707 } 1708 packageShortcutsChanged(packageName, userId); 1709 1710 verifyStates(); 1711 } 1712 1713 @Override 1714 public void removeDynamicShortcuts(String packageName, List shortcutIds, 1715 @UserIdInt int userId) { 1716 verifyCaller(packageName, userId); 1717 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1718 1719 synchronized (mLock) { 1720 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1721 1722 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1723 1724 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1725 ps.deleteDynamicWithId( 1726 Preconditions.checkStringNotEmpty((String) shortcutIds.get(i))); 1727 } 1728 1729 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. 1730 ps.adjustRanks(); 1731 } 1732 packageShortcutsChanged(packageName, userId); 1733 1734 verifyStates(); 1735 } 1736 1737 @Override 1738 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { 1739 verifyCaller(packageName, userId); 1740 1741 synchronized (mLock) { 1742 getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(); 1743 } 1744 packageShortcutsChanged(packageName, userId); 1745 1746 verifyStates(); 1747 } 1748 1749 @Override 1750 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, 1751 @UserIdInt int userId) { 1752 verifyCaller(packageName, userId); 1753 synchronized (mLock) { 1754 return getShortcutsWithQueryLocked( 1755 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1756 ShortcutInfo::isDynamic); 1757 } 1758 } 1759 1760 @Override 1761 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, 1762 @UserIdInt int userId) { 1763 verifyCaller(packageName, userId); 1764 synchronized (mLock) { 1765 return getShortcutsWithQueryLocked( 1766 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1767 ShortcutInfo::isManifestShortcut); 1768 } 1769 } 1770 1771 @Override 1772 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, 1773 @UserIdInt int userId) { 1774 verifyCaller(packageName, userId); 1775 synchronized (mLock) { 1776 return getShortcutsWithQueryLocked( 1777 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1778 ShortcutInfo::isPinned); 1779 } 1780 } 1781 1782 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, 1783 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { 1784 1785 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 1786 1787 getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags); 1788 1789 return new ParceledListSlice<>(ret); 1790 } 1791 1792 @Override 1793 public int getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId) 1794 throws RemoteException { 1795 verifyCaller(packageName, userId); 1796 1797 return mMaxShortcuts; 1798 } 1799 1800 @Override 1801 public int getRemainingCallCount(String packageName, @UserIdInt int userId) { 1802 verifyCaller(packageName, userId); 1803 1804 synchronized (mLock) { 1805 return mMaxUpdatesPerInterval 1806 - getPackageShortcutsLocked(packageName, userId).getApiCallCount(); 1807 } 1808 } 1809 1810 @Override 1811 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { 1812 verifyCaller(packageName, userId); 1813 1814 synchronized (mLock) { 1815 return getNextResetTimeLocked(); 1816 } 1817 } 1818 1819 @Override 1820 public int getIconMaxDimensions(String packageName, int userId) { 1821 verifyCaller(packageName, userId); 1822 1823 synchronized (mLock) { 1824 return mMaxIconDimension; 1825 } 1826 } 1827 1828 @Override 1829 public void reportShortcutUsed(String packageName, String shortcutId, int userId) { 1830 verifyCaller(packageName, userId); 1831 1832 Preconditions.checkNotNull(shortcutId); 1833 1834 if (DEBUG) { 1835 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", 1836 shortcutId, packageName, userId)); 1837 } 1838 1839 synchronized (mLock) { 1840 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1841 if (ps.findShortcutById(shortcutId) == null) { 1842 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", 1843 packageName, shortcutId)); 1844 return; 1845 } 1846 } 1847 1848 final long token = injectClearCallingIdentity(); 1849 try { 1850 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); 1851 } finally { 1852 injectRestoreCallingIdentity(token); 1853 } 1854 } 1855 1856 /** 1857 * Reset all throttling, for developer options and command line. Only system/shell can call 1858 * it. 1859 */ 1860 @Override 1861 public void resetThrottling() { 1862 enforceSystemOrShell(); 1863 1864 resetThrottlingInner(getCallingUserId()); 1865 } 1866 1867 void resetThrottlingInner(@UserIdInt int userId) { 1868 synchronized (mLock) { 1869 getUserShortcutsLocked(userId).resetThrottling(); 1870 } 1871 scheduleSaveUser(userId); 1872 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId); 1873 } 1874 1875 void resetAllThrottlingInner() { 1876 synchronized (mLock) { 1877 mRawLastResetTime = injectCurrentTimeMillis(); 1878 } 1879 scheduleSaveBaseState(); 1880 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users"); 1881 } 1882 1883 void resetPackageThrottling(String packageName, int userId) { 1884 synchronized (mLock) { 1885 getPackageShortcutsLocked(packageName, userId) 1886 .resetRateLimitingForCommandLineNoSaving(); 1887 saveUserLocked(userId); 1888 } 1889 } 1890 1891 @Override 1892 public void onApplicationActive(String packageName, int userId) { 1893 if (DEBUG) { 1894 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); 1895 } 1896 enforceResetThrottlingPermission(); 1897 resetPackageThrottling(packageName, userId); 1898 } 1899 1900 // We override this method in unit tests to do a simpler check. 1901 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { 1902 return hasShortcutHostPermissionInner(callingPackage, userId); 1903 } 1904 1905 // This method is extracted so we can directly call this method from unit tests, 1906 // even when hasShortcutPermission() is overridden. 1907 @VisibleForTesting 1908 boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { 1909 synchronized (mLock) { 1910 final long start = injectElapsedRealtime(); 1911 1912 final ShortcutUser user = getUserShortcutsLocked(userId); 1913 1914 final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); 1915 1916 // Default launcher from package manager. 1917 final long startGetHomeActivitiesAsUser = injectElapsedRealtime(); 1918 final ComponentName defaultLauncher = injectPackageManagerInternal() 1919 .getHomeActivitiesAsUser(allHomeCandidates, userId); 1920 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); 1921 1922 ComponentName detected; 1923 if (defaultLauncher != null) { 1924 detected = defaultLauncher; 1925 if (DEBUG) { 1926 Slog.v(TAG, "Default launcher from PM: " + detected); 1927 } 1928 } else { 1929 detected = user.getDefaultLauncherComponent(); 1930 1931 if (detected != null) { 1932 if (injectIsActivityEnabledAndExported(detected, userId)) { 1933 if (DEBUG) { 1934 Slog.v(TAG, "Cached launcher: " + detected); 1935 } 1936 } else { 1937 Slog.w(TAG, "Cached launcher " + detected + " no longer exists"); 1938 detected = null; 1939 user.setDefaultLauncherComponent(null); 1940 } 1941 } 1942 } 1943 1944 if (detected == null) { 1945 // If we reach here, that means it's the first check since the user was created, 1946 // and there's already multiple launchers and there's no default set. 1947 // Find the system one with the highest priority. 1948 // (We need to check the priority too because of FallbackHome in Settings.) 1949 // If there's no system launcher yet, then no one can access shortcuts, until 1950 // the user explicitly 1951 final int size = allHomeCandidates.size(); 1952 1953 int lastPriority = Integer.MIN_VALUE; 1954 for (int i = 0; i < size; i++) { 1955 final ResolveInfo ri = allHomeCandidates.get(i); 1956 if (!ri.activityInfo.applicationInfo.isSystemApp()) { 1957 continue; 1958 } 1959 if (DEBUG) { 1960 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d", 1961 ri.activityInfo.getComponentName(), ri.priority)); 1962 } 1963 if (ri.priority < lastPriority) { 1964 continue; 1965 } 1966 detected = ri.activityInfo.getComponentName(); 1967 lastPriority = ri.priority; 1968 } 1969 } 1970 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); 1971 1972 if (detected != null) { 1973 if (DEBUG) { 1974 Slog.v(TAG, "Detected launcher: " + detected); 1975 } 1976 user.setDefaultLauncherComponent(detected); 1977 return detected.getPackageName().equals(callingPackage); 1978 } else { 1979 // Default launcher not found. 1980 return false; 1981 } 1982 } 1983 } 1984 1985 // === House keeping === 1986 1987 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId, 1988 boolean appStillExists) { 1989 synchronized (mLock) { 1990 forEachLoadedUserLocked(user -> 1991 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId, 1992 appStillExists)); 1993 } 1994 } 1995 1996 /** 1997 * Remove all the information associated with a package. This will really remove all the 1998 * information, including the restore information (i.e. it'll remove packages even if they're 1999 * shadow). 2000 * 2001 * This is called when an app is uninstalled, or an app gets "clear data"ed. 2002 */ 2003 @VisibleForTesting 2004 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId, 2005 boolean appStillExists) { 2006 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId); 2007 2008 final ShortcutUser user = getUserShortcutsLocked(owningUserId); 2009 boolean doNotify = false; 2010 2011 // First, remove the package from the package list (if the package is a publisher). 2012 if (packageUserId == owningUserId) { 2013 if (user.removePackage(packageName) != null) { 2014 doNotify = true; 2015 } 2016 } 2017 2018 // Also remove from the launcher list (if the package is a launcher). 2019 user.removeLauncher(packageUserId, packageName); 2020 2021 // Then remove pinned shortcuts from all launchers. 2022 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId)); 2023 2024 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous 2025 // step. Remove them too. 2026 user.forAllPackages(p -> p.refreshPinnedFlags()); 2027 2028 scheduleSaveUser(owningUserId); 2029 2030 if (doNotify) { 2031 notifyListeners(packageName, owningUserId); 2032 } 2033 2034 // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts. 2035 if (appStillExists && (packageUserId == owningUserId)) { 2036 // This will do the notification and save when needed, so do it after the above 2037 // notifyListeners. 2038 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ true); 2039 } 2040 2041 if (!wasUserLoaded) { 2042 // Note this will execute the scheduled save. 2043 unloadUserLocked(owningUserId); 2044 } 2045 } 2046 2047 /** 2048 * Entry point from {@link LauncherApps}. 2049 */ 2050 private class LocalService extends ShortcutServiceInternal { 2051 2052 @Override 2053 public List<ShortcutInfo> getShortcuts(int launcherUserId, 2054 @NonNull String callingPackage, long changedSince, 2055 @Nullable String packageName, @Nullable List<String> shortcutIds, 2056 @Nullable ComponentName componentName, 2057 int queryFlags, int userId) { 2058 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 2059 final boolean cloneKeyFieldOnly = 2060 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); 2061 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO 2062 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; 2063 if (packageName == null) { 2064 shortcutIds = null; // LauncherAppsService already threw for it though. 2065 } 2066 2067 synchronized (mLock) { 2068 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2069 .attemptToRestoreIfNeededAndSave(); 2070 2071 if (packageName != null) { 2072 getShortcutsInnerLocked(launcherUserId, 2073 callingPackage, packageName, shortcutIds, changedSince, 2074 componentName, queryFlags, userId, ret, cloneFlag); 2075 } else { 2076 final List<String> shortcutIdsF = shortcutIds; 2077 getUserShortcutsLocked(userId).forAllPackages(p -> { 2078 getShortcutsInnerLocked(launcherUserId, 2079 callingPackage, p.getPackageName(), shortcutIdsF, changedSince, 2080 componentName, queryFlags, userId, ret, cloneFlag); 2081 }); 2082 } 2083 } 2084 return ret; 2085 } 2086 2087 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, 2088 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, 2089 @Nullable ComponentName componentName, int queryFlags, 2090 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { 2091 final ArraySet<String> ids = shortcutIds == null ? null 2092 : new ArraySet<>(shortcutIds); 2093 2094 final ShortcutPackage p = getUserShortcutsLocked(userId) 2095 .getPackageShortcutsIfExists(packageName); 2096 if (p == null) { 2097 return; // No need to instantiate ShortcutPackage. 2098 } 2099 2100 p.findAll(ret, 2101 (ShortcutInfo si) -> { 2102 if (si.getLastChangedTimestamp() < changedSince) { 2103 return false; 2104 } 2105 if (ids != null && !ids.contains(si.getId())) { 2106 return false; 2107 } 2108 if (componentName != null) { 2109 if (si.getActivity() != null 2110 && !si.getActivity().equals(componentName)) { 2111 return false; 2112 } 2113 } 2114 if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0) 2115 && si.isDynamic()) { 2116 return true; 2117 } 2118 if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0) 2119 && si.isPinned()) { 2120 return true; 2121 } 2122 if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0) 2123 && si.isManifestShortcut()) { 2124 return true; 2125 } 2126 return false; 2127 }, cloneFlag, callingPackage, launcherUserId); 2128 } 2129 2130 @Override 2131 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, 2132 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2133 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2134 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2135 2136 synchronized (mLock) { 2137 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2138 .attemptToRestoreIfNeededAndSave(); 2139 2140 final ShortcutInfo si = getShortcutInfoLocked( 2141 launcherUserId, callingPackage, packageName, shortcutId, userId); 2142 return si != null && si.isPinned(); 2143 } 2144 } 2145 2146 private ShortcutInfo getShortcutInfoLocked( 2147 int launcherUserId, @NonNull String callingPackage, 2148 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2149 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2150 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2151 2152 final ShortcutPackage p = getUserShortcutsLocked(userId) 2153 .getPackageShortcutsIfExists(packageName); 2154 if (p == null) { 2155 return null; 2156 } 2157 2158 final ArrayList<ShortcutInfo> list = new ArrayList<>(1); 2159 p.findAll(list, 2160 (ShortcutInfo si) -> shortcutId.equals(si.getId()), 2161 /* clone flags=*/ 0, callingPackage, launcherUserId); 2162 return list.size() == 0 ? null : list.get(0); 2163 } 2164 2165 @Override 2166 public void pinShortcuts(int launcherUserId, 2167 @NonNull String callingPackage, @NonNull String packageName, 2168 @NonNull List<String> shortcutIds, int userId) { 2169 // Calling permission must be checked by LauncherAppsImpl. 2170 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2171 Preconditions.checkNotNull(shortcutIds, "shortcutIds"); 2172 2173 synchronized (mLock) { 2174 final ShortcutLauncher launcher = 2175 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); 2176 launcher.attemptToRestoreIfNeededAndSave(); 2177 2178 launcher.pinShortcuts(userId, packageName, shortcutIds); 2179 } 2180 packageShortcutsChanged(packageName, userId); 2181 2182 verifyStates(); 2183 } 2184 2185 @Override 2186 public Intent createShortcutIntent(int launcherUserId, 2187 @NonNull String callingPackage, 2188 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2189 // Calling permission must be checked by LauncherAppsImpl. 2190 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); 2191 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); 2192 2193 synchronized (mLock) { 2194 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2195 .attemptToRestoreIfNeededAndSave(); 2196 2197 // Make sure the shortcut is actually visible to the launcher. 2198 final ShortcutInfo si = getShortcutInfoLocked( 2199 launcherUserId, callingPackage, packageName, shortcutId, userId); 2200 // "si == null" should suffice here, but check the flags too just to make sure. 2201 if (si == null || !si.isEnabled() || !si.isAlive()) { 2202 Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled"); 2203 return null; 2204 } 2205 return si.getIntent(); 2206 } 2207 } 2208 2209 @Override 2210 public void addListener(@NonNull ShortcutChangeListener listener) { 2211 synchronized (mLock) { 2212 mListeners.add(Preconditions.checkNotNull(listener)); 2213 } 2214 } 2215 2216 @Override 2217 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, 2218 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2219 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2220 Preconditions.checkNotNull(packageName, "packageName"); 2221 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2222 2223 synchronized (mLock) { 2224 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2225 .attemptToRestoreIfNeededAndSave(); 2226 2227 final ShortcutPackage p = getUserShortcutsLocked(userId) 2228 .getPackageShortcutsIfExists(packageName); 2229 if (p == null) { 2230 return 0; 2231 } 2232 2233 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2234 return (shortcutInfo != null && shortcutInfo.hasIconResource()) 2235 ? shortcutInfo.getIconResourceId() : 0; 2236 } 2237 } 2238 2239 @Override 2240 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId, 2241 @NonNull String callingPackage, @NonNull String packageName, 2242 @NonNull String shortcutId, int userId) { 2243 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2244 Preconditions.checkNotNull(packageName, "packageName"); 2245 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2246 2247 synchronized (mLock) { 2248 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2249 .attemptToRestoreIfNeededAndSave(); 2250 2251 final ShortcutPackage p = getUserShortcutsLocked(userId) 2252 .getPackageShortcutsIfExists(packageName); 2253 if (p == null) { 2254 return null; 2255 } 2256 2257 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2258 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) { 2259 return null; 2260 } 2261 try { 2262 if (shortcutInfo.getBitmapPath() == null) { 2263 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()"); 2264 return null; 2265 } 2266 return ParcelFileDescriptor.open( 2267 new File(shortcutInfo.getBitmapPath()), 2268 ParcelFileDescriptor.MODE_READ_ONLY); 2269 } catch (FileNotFoundException e) { 2270 Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath()); 2271 return null; 2272 } 2273 } 2274 } 2275 2276 @Override 2277 public boolean hasShortcutHostPermission(int launcherUserId, 2278 @NonNull String callingPackage) { 2279 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId); 2280 } 2281 2282 /** 2283 * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take 2284 * any locks in this method. 2285 */ 2286 @Override 2287 public void onSystemLocaleChangedNoLock() { 2288 // DO NOT HOLD ANY LOCKS HERE. 2289 2290 // We want to reset throttling for all packages for all users. But we can't just do so 2291 // here because: 2292 // - We can't load/save users that are locked. 2293 // - Even for loaded users, resetting the counters would require us to hold mLock. 2294 // 2295 // So we use a "pull" model instead. In here, we just increment the "locale change 2296 // sequence number". Each ShortcutUser has the "last known locale change sequence". 2297 // 2298 // This allows ShortcutUser's to detect the system locale change, so they can reset 2299 // counters. 2300 2301 // Ignore all callback during system boot. 2302 if (mBootCompleted.get()) { 2303 mLocaleChangeSequenceNumber.incrementAndGet(); 2304 if (DEBUG) { 2305 Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get()); 2306 } 2307 injectPostToHandler(() -> handleLocaleChanged()); 2308 } 2309 } 2310 } 2311 2312 void handleLocaleChanged() { 2313 if (DEBUG) { 2314 Slog.d(TAG, "handleLocaleChanged"); 2315 } 2316 scheduleSaveBaseState(); 2317 2318 final long token = injectClearCallingIdentity(); 2319 try { 2320 forEachLoadedUserLocked(u -> u.forAllPackages(p -> p.resolveResourceStrings())); 2321 } finally { 2322 injectRestoreCallingIdentity(token); 2323 } 2324 } 2325 2326 /** 2327 * Package event callbacks. 2328 */ 2329 @VisibleForTesting 2330 final PackageMonitor mPackageMonitor = new PackageMonitor() { 2331 2332 private boolean isUserUnlocked() { 2333 return mUserManager.isUserUnlocked(getChangingUserId()); 2334 } 2335 2336 @Override 2337 public void onReceive(Context context, Intent intent) { 2338 // clearCallingIdentity is not needed normally, but need to do it for the unit test. 2339 final long token = injectClearCallingIdentity(); 2340 try { 2341 super.onReceive(context, intent); 2342 } finally { 2343 injectRestoreCallingIdentity(token); 2344 } 2345 } 2346 2347 @Override 2348 public void onPackageAdded(String packageName, int uid) { 2349 if (!isUserUnlocked()) return; 2350 handlePackageAdded(packageName, getChangingUserId()); 2351 } 2352 2353 @Override 2354 public void onPackageUpdateFinished(String packageName, int uid) { 2355 if (!isUserUnlocked()) return; 2356 handlePackageUpdateFinished(packageName, getChangingUserId()); 2357 } 2358 2359 @Override 2360 public void onPackageRemoved(String packageName, int uid) { 2361 if (!isUserUnlocked()) return; 2362 handlePackageRemoved(packageName, getChangingUserId()); 2363 } 2364 2365 @Override 2366 public void onPackageDataCleared(String packageName, int uid) { 2367 if (!isUserUnlocked()) return; 2368 handlePackageDataCleared(packageName, getChangingUserId()); 2369 } 2370 2371 @Override 2372 public boolean onPackageChanged(String packageName, int uid, String[] components) { 2373 if (!isUserUnlocked()) return false; 2374 handlePackageChanged(packageName, getChangingUserId()); 2375 return false; // We don't need to receive onSomePackagesChanged(), so just false. 2376 } 2377 }; 2378 2379 /** 2380 * Called when a user is unlocked. 2381 * - Check all known packages still exist, and otherwise perform cleanup. 2382 * - If a package still exists, check the version code. If it's been updated, may need to 2383 * update timestamps of its shortcuts. 2384 */ 2385 @VisibleForTesting 2386 void checkPackageChanges(@UserIdInt int ownerUserId) { 2387 if (DEBUG) { 2388 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId); 2389 } 2390 if (injectIsSafeModeEnabled()) { 2391 Slog.i(TAG, "Safe mode, skipping checkPackageChanges()"); 2392 return; 2393 } 2394 2395 final long start = injectElapsedRealtime(); 2396 try { 2397 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>(); 2398 2399 synchronized (mLock) { 2400 final ShortcutUser user = getUserShortcutsLocked(ownerUserId); 2401 2402 // Find packages that have been uninstalled. 2403 user.forAllPackageItems(spi -> { 2404 if (spi.getPackageInfo().isShadow()) { 2405 return; // Don't delete shadow information. 2406 } 2407 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) { 2408 if (DEBUG) { 2409 Slog.d(TAG, "Uninstalled: " + spi.getPackageName() 2410 + " user " + spi.getPackageUserId()); 2411 } 2412 gonePackages.add(PackageWithUser.of(spi)); 2413 } 2414 }); 2415 if (gonePackages.size() > 0) { 2416 for (int i = gonePackages.size() - 1; i >= 0; i--) { 2417 final PackageWithUser pu = gonePackages.get(i); 2418 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId, 2419 /* appStillExists = */ false); 2420 } 2421 } 2422 final long now = injectCurrentTimeMillis(); 2423 2424 // Then for each installed app, publish manifest shortcuts when needed. 2425 forUpdatedPackages(ownerUserId, user.getLastAppScanTime(), ai -> { 2426 user.handlePackageAddedOrUpdated(ai.packageName, /* forceRescan=*/ false); 2427 }); 2428 2429 // Write the time just before the scan, because there may be apps that have just 2430 // been updated, and we want to catch them in the next time. 2431 user.setLastAppScanTime(now); 2432 scheduleSaveUser(ownerUserId); 2433 } 2434 } finally { 2435 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start); 2436 } 2437 verifyStates(); 2438 } 2439 2440 private void handlePackageAdded(String packageName, @UserIdInt int userId) { 2441 if (DEBUG) { 2442 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId)); 2443 } 2444 synchronized (mLock) { 2445 final ShortcutUser user = getUserShortcutsLocked(userId); 2446 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 2447 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ false); 2448 } 2449 verifyStates(); 2450 } 2451 2452 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) { 2453 if (DEBUG) { 2454 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d", 2455 packageName, userId)); 2456 } 2457 synchronized (mLock) { 2458 final ShortcutUser user = getUserShortcutsLocked(userId); 2459 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 2460 2461 if (isPackageInstalled(packageName, userId)) { 2462 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ false); 2463 } 2464 } 2465 verifyStates(); 2466 } 2467 2468 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) { 2469 if (DEBUG) { 2470 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, 2471 packageUserId)); 2472 } 2473 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false); 2474 2475 verifyStates(); 2476 } 2477 2478 private void handlePackageDataCleared(String packageName, int packageUserId) { 2479 if (DEBUG) { 2480 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName, 2481 packageUserId)); 2482 } 2483 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true); 2484 2485 verifyStates(); 2486 } 2487 2488 private void handlePackageChanged(String packageName, int packageUserId) { 2489 if (DEBUG) { 2490 Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName, 2491 packageUserId)); 2492 } 2493 2494 // Activities may be disabled or enabled. Just rescan the package. 2495 synchronized (mLock) { 2496 final ShortcutUser user = getUserShortcutsLocked(packageUserId); 2497 2498 user.handlePackageAddedOrUpdated(packageName, /* forceRescan=*/ true); 2499 } 2500 2501 verifyStates(); 2502 } 2503 2504 // === PackageManager interaction === 2505 2506 /** 2507 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 2508 */ 2509 @Nullable 2510 final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { 2511 return getPackageInfo(packageName, userId, true); 2512 } 2513 2514 /** 2515 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 2516 */ 2517 @Nullable 2518 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { 2519 return getPackageInfo(packageName, userId, false); 2520 } 2521 2522 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { 2523 final long token = injectClearCallingIdentity(); 2524 try { 2525 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId); 2526 } catch (RemoteException e) { 2527 // Shouldn't happen. 2528 Slog.wtf(TAG, "RemoteException", e); 2529 return -1; 2530 } finally { 2531 injectRestoreCallingIdentity(token); 2532 } 2533 } 2534 2535 /** 2536 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 2537 */ 2538 @Nullable 2539 @VisibleForTesting 2540 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId, 2541 boolean getSignatures) { 2542 return isInstalledOrNull(injectPackageInfoWithUninstalled( 2543 packageName, userId, getSignatures)); 2544 } 2545 2546 /** 2547 * Do not use directly; this returns uninstalled packages too. 2548 */ 2549 @Nullable 2550 @VisibleForTesting 2551 PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId, 2552 boolean getSignatures) { 2553 final long start = injectElapsedRealtime(); 2554 final long token = injectClearCallingIdentity(); 2555 try { 2556 return mIPackageManager.getPackageInfo( 2557 packageName, PACKAGE_MATCH_FLAGS 2558 | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId); 2559 } catch (RemoteException e) { 2560 // Shouldn't happen. 2561 Slog.wtf(TAG, "RemoteException", e); 2562 return null; 2563 } finally { 2564 injectRestoreCallingIdentity(token); 2565 2566 logDurationStat( 2567 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO), 2568 start); 2569 } 2570 } 2571 2572 /** 2573 * Returns {@link ApplicationInfo} unless it's uninstalled or disabled. 2574 */ 2575 @Nullable 2576 @VisibleForTesting 2577 final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) { 2578 return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId)); 2579 } 2580 2581 /** 2582 * Do not use directly; this returns uninstalled packages too. 2583 */ 2584 @Nullable 2585 @VisibleForTesting 2586 ApplicationInfo injectApplicationInfoWithUninstalled( 2587 String packageName, @UserIdInt int userId) { 2588 final long start = injectElapsedRealtime(); 2589 final long token = injectClearCallingIdentity(); 2590 try { 2591 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); 2592 } catch (RemoteException e) { 2593 // Shouldn't happen. 2594 Slog.wtf(TAG, "RemoteException", e); 2595 return null; 2596 } finally { 2597 injectRestoreCallingIdentity(token); 2598 2599 logDurationStat(Stats.GET_APPLICATION_INFO, start); 2600 } 2601 } 2602 2603 /** 2604 * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled. 2605 */ 2606 @Nullable 2607 final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) { 2608 return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled( 2609 activity, userId)); 2610 } 2611 2612 /** 2613 * Do not use directly; this returns uninstalled packages too. 2614 */ 2615 @Nullable 2616 @VisibleForTesting 2617 ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled( 2618 ComponentName activity, @UserIdInt int userId) { 2619 final long start = injectElapsedRealtime(); 2620 final long token = injectClearCallingIdentity(); 2621 try { 2622 return mIPackageManager.getActivityInfo(activity, 2623 (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId); 2624 } catch (RemoteException e) { 2625 // Shouldn't happen. 2626 Slog.wtf(TAG, "RemoteException", e); 2627 return null; 2628 } finally { 2629 injectRestoreCallingIdentity(token); 2630 2631 logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start); 2632 } 2633 } 2634 2635 /** 2636 * Return all installed and enabled packages. 2637 */ 2638 @NonNull 2639 @VisibleForTesting 2640 final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) { 2641 final long start = injectElapsedRealtime(); 2642 final long token = injectClearCallingIdentity(); 2643 try { 2644 final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId); 2645 2646 all.removeIf(PACKAGE_NOT_INSTALLED); 2647 2648 return all; 2649 } catch (RemoteException e) { 2650 // Shouldn't happen. 2651 Slog.wtf(TAG, "RemoteException", e); 2652 return null; 2653 } finally { 2654 injectRestoreCallingIdentity(token); 2655 2656 logDurationStat(Stats.GET_INSTALLED_PACKAGES, start); 2657 } 2658 } 2659 2660 /** 2661 * Do not use directly; this returns uninstalled packages too. 2662 */ 2663 @NonNull 2664 @VisibleForTesting 2665 List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId) 2666 throws RemoteException { 2667 final ParceledListSlice<PackageInfo> parceledList = 2668 mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId); 2669 if (parceledList == null) { 2670 return Collections.emptyList(); 2671 } 2672 return parceledList.getList(); 2673 } 2674 2675 private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, 2676 Consumer<ApplicationInfo> callback) { 2677 if (DEBUG) { 2678 Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime); 2679 } 2680 final List<PackageInfo> list = getInstalledPackages(userId); 2681 for (int i = list.size() - 1; i >= 0; i--) { 2682 final PackageInfo pi = list.get(i); 2683 2684 if (pi.lastUpdateTime >= lastScanTime) { 2685 if (DEBUG) { 2686 Slog.d(TAG, "Found updated package " + pi.packageName); 2687 } 2688 callback.accept(pi.applicationInfo); 2689 } 2690 } 2691 } 2692 2693 private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) { 2694 final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId); 2695 return (ai != null) && ((ai.flags & flags) == flags); 2696 } 2697 2698 private static boolean isInstalled(@Nullable ApplicationInfo ai) { 2699 return (ai != null) && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 2700 } 2701 2702 private static boolean isInstalled(@Nullable PackageInfo pi) { 2703 return (pi != null) && isInstalled(pi.applicationInfo); 2704 } 2705 2706 private static boolean isInstalled(@Nullable ActivityInfo ai) { 2707 return (ai != null) && isInstalled(ai.applicationInfo); 2708 } 2709 2710 private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) { 2711 return isInstalled(ai) ? ai : null; 2712 } 2713 2714 private static PackageInfo isInstalledOrNull(PackageInfo pi) { 2715 return isInstalled(pi) ? pi : null; 2716 } 2717 2718 private static ActivityInfo isInstalledOrNull(ActivityInfo ai) { 2719 return isInstalled(ai) ? ai : null; 2720 } 2721 2722 boolean isPackageInstalled(String packageName, int userId) { 2723 return getApplicationInfo(packageName, userId) != null; 2724 } 2725 2726 @Nullable 2727 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) { 2728 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key); 2729 } 2730 2731 @Nullable 2732 Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) { 2733 final long start = injectElapsedRealtime(); 2734 final long token = injectClearCallingIdentity(); 2735 try { 2736 return mContext.getPackageManager().getResourcesForApplicationAsUser( 2737 packageName, userId); 2738 } catch (NameNotFoundException e) { 2739 Slog.e(TAG, "Resources for package " + packageName + " not found"); 2740 return null; 2741 } finally { 2742 injectRestoreCallingIdentity(token); 2743 2744 logDurationStat(Stats.GET_APPLICATION_RESOURCES, start); 2745 } 2746 } 2747 2748 private Intent getMainActivityIntent() { 2749 final Intent intent = new Intent(Intent.ACTION_MAIN); 2750 intent.addCategory(LAUNCHER_INTENT_CATEGORY); 2751 return intent; 2752 } 2753 2754 /** 2755 * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed, 2756 * and only returns exported activities. 2757 */ 2758 @NonNull 2759 @VisibleForTesting 2760 List<ResolveInfo> queryActivities(@NonNull Intent baseIntent, 2761 @NonNull String packageName, @Nullable ComponentName activity, int userId) { 2762 2763 baseIntent.setPackage(Preconditions.checkNotNull(packageName)); 2764 if (activity != null) { 2765 baseIntent.setComponent(activity); 2766 } 2767 2768 final List<ResolveInfo> resolved = 2769 mContext.getPackageManager().queryIntentActivitiesAsUser( 2770 baseIntent, PACKAGE_MATCH_FLAGS, userId); 2771 if (resolved == null || resolved.size() == 0) { 2772 return EMPTY_RESOLVE_INFO; 2773 } 2774 // Make sure the package is installed. 2775 if (!isInstalled(resolved.get(0).activityInfo)) { 2776 return EMPTY_RESOLVE_INFO; 2777 } 2778 resolved.removeIf(ACTIVITY_NOT_EXPORTED); 2779 return resolved; 2780 } 2781 2782 /** 2783 * Return the main activity that is enabled and exported. If multiple activities are found, 2784 * return the first one. 2785 */ 2786 @Nullable 2787 ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) { 2788 final long start = injectElapsedRealtime(); 2789 final long token = injectClearCallingIdentity(); 2790 try { 2791 final List<ResolveInfo> resolved = 2792 queryActivities(getMainActivityIntent(), packageName, null, userId); 2793 return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName(); 2794 } finally { 2795 injectRestoreCallingIdentity(token); 2796 2797 logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start); 2798 } 2799 } 2800 2801 /** 2802 * Return whether an activity is enabled, exported and main. 2803 */ 2804 boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) { 2805 final long start = injectElapsedRealtime(); 2806 final long token = injectClearCallingIdentity(); 2807 try { 2808 final List<ResolveInfo> resolved = 2809 queryActivities(getMainActivityIntent(), activity.getPackageName(), 2810 activity, userId); 2811 return resolved.size() > 0; 2812 } finally { 2813 injectRestoreCallingIdentity(token); 2814 2815 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); 2816 } 2817 } 2818 2819 /** 2820 * Return all the enabled, exported and main activities from a package. 2821 */ 2822 @NonNull 2823 List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) { 2824 final long start = injectElapsedRealtime(); 2825 final long token = injectClearCallingIdentity(); 2826 try { 2827 return queryActivities(getMainActivityIntent(), packageName, null, userId); 2828 } finally { 2829 injectRestoreCallingIdentity(token); 2830 2831 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); 2832 } 2833 } 2834 2835 /** 2836 * Return whether an activity is enabled and exported. 2837 */ 2838 @VisibleForTesting 2839 boolean injectIsActivityEnabledAndExported( 2840 @NonNull ComponentName activity, @UserIdInt int userId) { 2841 final long start = injectElapsedRealtime(); 2842 final long token = injectClearCallingIdentity(); 2843 try { 2844 return queryActivities(new Intent(), activity.getPackageName(), activity, userId) 2845 .size() > 0; 2846 } finally { 2847 injectRestoreCallingIdentity(token); 2848 2849 logDurationStat(Stats.IS_ACTIVITY_ENABLED, start); 2850 } 2851 } 2852 2853 boolean injectIsSafeModeEnabled() { 2854 final long token = injectClearCallingIdentity(); 2855 try { 2856 return IWindowManager.Stub 2857 .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)) 2858 .isSafeModeEnabled(); 2859 } catch (RemoteException e) { 2860 return false; // Shouldn't happen though. 2861 } finally { 2862 injectRestoreCallingIdentity(token); 2863 } 2864 } 2865 2866 // === Backup & restore === 2867 2868 boolean shouldBackupApp(String packageName, int userId) { 2869 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP); 2870 } 2871 2872 boolean shouldBackupApp(PackageInfo pi) { 2873 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; 2874 } 2875 2876 @Override 2877 public byte[] getBackupPayload(@UserIdInt int userId) { 2878 enforceSystem(); 2879 if (DEBUG) { 2880 Slog.d(TAG, "Backing up user " + userId); 2881 } 2882 synchronized (mLock) { 2883 final ShortcutUser user = getUserShortcutsLocked(userId); 2884 if (user == null) { 2885 Slog.w(TAG, "Can't backup: user not found: id=" + userId); 2886 return null; 2887 } 2888 2889 user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave()); 2890 2891 // Then save. 2892 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024); 2893 try { 2894 saveUserInternalLocked(userId, os, /* forBackup */ true); 2895 } catch (XmlPullParserException | IOException e) { 2896 // Shouldn't happen. 2897 Slog.w(TAG, "Backup failed.", e); 2898 return null; 2899 } 2900 return os.toByteArray(); 2901 } 2902 } 2903 2904 @Override 2905 public void applyRestore(byte[] payload, @UserIdInt int userId) { 2906 enforceSystem(); 2907 if (DEBUG) { 2908 Slog.d(TAG, "Restoring user " + userId); 2909 } 2910 final ShortcutUser user; 2911 final ByteArrayInputStream is = new ByteArrayInputStream(payload); 2912 try { 2913 user = loadUserInternal(userId, is, /* fromBackup */ true); 2914 } catch (XmlPullParserException | IOException e) { 2915 Slog.w(TAG, "Restoration failed.", e); 2916 return; 2917 } 2918 synchronized (mLock) { 2919 mUsers.put(userId, user); 2920 2921 // Then purge all the save images. 2922 final File bitmapPath = getUserBitmapFilePath(userId); 2923 final boolean success = FileUtils.deleteContents(bitmapPath); 2924 if (!success) { 2925 Slog.w(TAG, "Failed to delete " + bitmapPath); 2926 } 2927 2928 saveUserLocked(userId); 2929 } 2930 } 2931 2932 // === Dump === 2933 2934 @Override 2935 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2936 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2937 != PackageManager.PERMISSION_GRANTED) { 2938 pw.println("Permission Denial: can't dump UserManager from from pid=" 2939 + Binder.getCallingPid() 2940 + ", uid=" + Binder.getCallingUid() 2941 + " without permission " 2942 + android.Manifest.permission.DUMP); 2943 return; 2944 } 2945 dumpInner(pw, args); 2946 } 2947 2948 @VisibleForTesting 2949 void dumpInner(PrintWriter pw, String[] args) { 2950 synchronized (mLock) { 2951 final long now = injectCurrentTimeMillis(); 2952 pw.print("Now: ["); 2953 pw.print(now); 2954 pw.print("] "); 2955 pw.print(formatTime(now)); 2956 2957 pw.print(" Raw last reset: ["); 2958 pw.print(mRawLastResetTime); 2959 pw.print("] "); 2960 pw.print(formatTime(mRawLastResetTime)); 2961 2962 final long last = getLastResetTimeLocked(); 2963 pw.print(" Last reset: ["); 2964 pw.print(last); 2965 pw.print("] "); 2966 pw.print(formatTime(last)); 2967 2968 final long next = getNextResetTimeLocked(); 2969 pw.print(" Next reset: ["); 2970 pw.print(next); 2971 pw.print("] "); 2972 pw.print(formatTime(next)); 2973 2974 pw.print(" Locale change seq#: "); 2975 pw.print(mLocaleChangeSequenceNumber.get()); 2976 pw.println(); 2977 2978 pw.print(" Config:"); 2979 pw.print(" Max icon dim: "); 2980 pw.println(mMaxIconDimension); 2981 pw.print(" Icon format: "); 2982 pw.println(mIconPersistFormat); 2983 pw.print(" Icon quality: "); 2984 pw.println(mIconPersistQuality); 2985 pw.print(" saveDelayMillis: "); 2986 pw.println(mSaveDelayMillis); 2987 pw.print(" resetInterval: "); 2988 pw.println(mResetInterval); 2989 pw.print(" maxUpdatesPerInterval: "); 2990 pw.println(mMaxUpdatesPerInterval); 2991 pw.print(" maxShortcutsPerActivity: "); 2992 pw.println(mMaxShortcuts); 2993 pw.println(); 2994 2995 pw.println(" Stats:"); 2996 synchronized (mStatLock) { 2997 final String p = " "; 2998 dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()"); 2999 dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check"); 3000 3001 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()"); 3002 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)"); 3003 dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo"); 3004 dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps"); 3005 dumpStatLS(pw, p, Stats.GET_ACTIVITY_WITH_METADATA, "getActivity+metadata"); 3006 dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages"); 3007 dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges"); 3008 dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources"); 3009 dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup"); 3010 dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity"); 3011 dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity"); 3012 dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled"); 3013 } 3014 3015 pw.println(); 3016 pw.print(" #Failures: "); 3017 pw.println(mWtfCount); 3018 3019 if (mLastWtfStacktrace != null) { 3020 pw.print(" Last failure stack trace: "); 3021 pw.println(Log.getStackTraceString(mLastWtfStacktrace)); 3022 } 3023 3024 for (int i = 0; i < mUsers.size(); i++) { 3025 pw.println(); 3026 mUsers.valueAt(i).dump(pw, " "); 3027 } 3028 3029 pw.println(); 3030 pw.println(" UID state:"); 3031 3032 for (int i = 0; i < mUidState.size(); i++) { 3033 final int uid = mUidState.keyAt(i); 3034 final int state = mUidState.valueAt(i); 3035 pw.print(" UID="); 3036 pw.print(uid); 3037 pw.print(" state="); 3038 pw.print(state); 3039 if (isProcessStateForeground(state)) { 3040 pw.print(" [FG]"); 3041 } 3042 pw.print(" last FG="); 3043 pw.print(mUidLastForegroundElapsedTime.get(uid)); 3044 pw.println(); 3045 } 3046 } 3047 } 3048 3049 static String formatTime(long time) { 3050 Time tobj = new Time(); 3051 tobj.set(time); 3052 return tobj.format("%Y-%m-%d %H:%M:%S"); 3053 } 3054 3055 private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) { 3056 pw.print(prefix); 3057 final int count = mCountStats[statId]; 3058 final long dur = mDurationStats[statId]; 3059 pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms", 3060 label, count, dur, 3061 (count == 0 ? 0 : ((double) dur) / count))); 3062 } 3063 3064 // === Shell support === 3065 3066 @Override 3067 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 3068 String[] args, ResultReceiver resultReceiver) throws RemoteException { 3069 3070 enforceShell(); 3071 3072 final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); 3073 3074 resultReceiver.send(status, null); 3075 } 3076 3077 static class CommandException extends Exception { 3078 public CommandException(String message) { 3079 super(message); 3080 } 3081 } 3082 3083 /** 3084 * Handle "adb shell cmd". 3085 */ 3086 private class MyShellCommand extends ShellCommand { 3087 3088 private int mUserId = UserHandle.USER_SYSTEM; 3089 3090 private void parseOptions(boolean takeUser) 3091 throws CommandException { 3092 String opt; 3093 while ((opt = getNextOption()) != null) { 3094 switch (opt) { 3095 case "--user": 3096 if (takeUser) { 3097 mUserId = UserHandle.parseUserArg(getNextArgRequired()); 3098 if (!mUserManager.isUserUnlocked(mUserId)) { 3099 throw new CommandException( 3100 "User " + mUserId + " is not running or locked"); 3101 } 3102 break; 3103 } 3104 // fallthrough 3105 default: 3106 throw new CommandException("Unknown option: " + opt); 3107 } 3108 } 3109 } 3110 3111 @Override 3112 public int onCommand(String cmd) { 3113 if (cmd == null) { 3114 return handleDefaultCommands(cmd); 3115 } 3116 final PrintWriter pw = getOutPrintWriter(); 3117 try { 3118 switch (cmd) { 3119 case "reset-package-throttling": 3120 handleResetPackageThrottling(); 3121 break; 3122 case "reset-throttling": 3123 handleResetThrottling(); 3124 break; 3125 case "reset-all-throttling": 3126 handleResetAllThrottling(); 3127 break; 3128 case "override-config": 3129 handleOverrideConfig(); 3130 break; 3131 case "reset-config": 3132 handleResetConfig(); 3133 break; 3134 case "clear-default-launcher": 3135 handleClearDefaultLauncher(); 3136 break; 3137 case "get-default-launcher": 3138 handleGetDefaultLauncher(); 3139 break; 3140 case "refresh-default-launcher": 3141 handleRefreshDefaultLauncher(); 3142 break; 3143 case "unload-user": 3144 handleUnloadUser(); 3145 break; 3146 case "clear-shortcuts": 3147 handleClearShortcuts(); 3148 break; 3149 case "verify-states": // hidden command to verify various internal states. 3150 handleVerifyStates(); 3151 break; 3152 default: 3153 return handleDefaultCommands(cmd); 3154 } 3155 } catch (CommandException e) { 3156 pw.println("Error: " + e.getMessage()); 3157 return 1; 3158 } 3159 pw.println("Success"); 3160 return 0; 3161 } 3162 3163 @Override 3164 public void onHelp() { 3165 final PrintWriter pw = getOutPrintWriter(); 3166 pw.println("Usage: cmd shortcut COMMAND [options ...]"); 3167 pw.println(); 3168 pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE"); 3169 pw.println(" Reset throttling for a package"); 3170 pw.println(); 3171 pw.println("cmd shortcut reset-throttling [--user USER_ID]"); 3172 pw.println(" Reset throttling for all packages and users"); 3173 pw.println(); 3174 pw.println("cmd shortcut reset-all-throttling"); 3175 pw.println(" Reset the throttling state for all users"); 3176 pw.println(); 3177 pw.println("cmd shortcut override-config CONFIG"); 3178 pw.println(" Override the configuration for testing (will last until reboot)"); 3179 pw.println(); 3180 pw.println("cmd shortcut reset-config"); 3181 pw.println(" Reset the configuration set with \"update-config\""); 3182 pw.println(); 3183 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]"); 3184 pw.println(" Clear the cached default launcher"); 3185 pw.println(); 3186 pw.println("cmd shortcut get-default-launcher [--user USER_ID]"); 3187 pw.println(" Show the cached default launcher"); 3188 pw.println(); 3189 pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]"); 3190 pw.println(" Refresh the cached default launcher"); 3191 pw.println(); 3192 pw.println("cmd shortcut unload-user [--user USER_ID]"); 3193 pw.println(" Unload a user from the memory"); 3194 pw.println(" (This should not affect any observable behavior)"); 3195 pw.println(); 3196 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE"); 3197 pw.println(" Remove all shortcuts from a package, including pinned shortcuts"); 3198 pw.println(); 3199 } 3200 3201 private void handleResetThrottling() throws CommandException { 3202 parseOptions(/* takeUser =*/ true); 3203 3204 Slog.i(TAG, "cmd: handleResetThrottling"); 3205 3206 resetThrottlingInner(mUserId); 3207 } 3208 3209 private void handleResetAllThrottling() { 3210 Slog.i(TAG, "cmd: handleResetAllThrottling"); 3211 3212 resetAllThrottlingInner(); 3213 } 3214 3215 private void handleResetPackageThrottling() throws CommandException { 3216 parseOptions(/* takeUser =*/ true); 3217 3218 final String packageName = getNextArgRequired(); 3219 3220 Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName); 3221 3222 resetPackageThrottling(packageName, mUserId); 3223 } 3224 3225 private void handleOverrideConfig() throws CommandException { 3226 final String config = getNextArgRequired(); 3227 3228 Slog.i(TAG, "cmd: handleOverrideConfig: " + config); 3229 3230 synchronized (mLock) { 3231 if (!updateConfigurationLocked(config)) { 3232 throw new CommandException("override-config failed. See logcat for details."); 3233 } 3234 } 3235 } 3236 3237 private void handleResetConfig() { 3238 Slog.i(TAG, "cmd: handleResetConfig"); 3239 3240 synchronized (mLock) { 3241 loadConfigurationLocked(); 3242 } 3243 } 3244 3245 private void clearLauncher() { 3246 synchronized (mLock) { 3247 getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null); 3248 } 3249 } 3250 3251 private void showLauncher() { 3252 synchronized (mLock) { 3253 // This ensures to set the cached launcher. Package name doesn't matter. 3254 hasShortcutHostPermissionInner("-", mUserId); 3255 3256 getOutPrintWriter().println("Launcher: " 3257 + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent()); 3258 } 3259 } 3260 3261 private void handleClearDefaultLauncher() throws CommandException { 3262 parseOptions(/* takeUser =*/ true); 3263 3264 clearLauncher(); 3265 } 3266 3267 private void handleGetDefaultLauncher() throws CommandException { 3268 parseOptions(/* takeUser =*/ true); 3269 3270 showLauncher(); 3271 } 3272 3273 private void handleRefreshDefaultLauncher() throws CommandException { 3274 parseOptions(/* takeUser =*/ true); 3275 3276 clearLauncher(); 3277 showLauncher(); 3278 } 3279 3280 private void handleUnloadUser() throws CommandException { 3281 parseOptions(/* takeUser =*/ true); 3282 3283 Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId); 3284 3285 ShortcutService.this.handleCleanupUser(mUserId); 3286 } 3287 3288 private void handleClearShortcuts() throws CommandException { 3289 parseOptions(/* takeUser =*/ true); 3290 final String packageName = getNextArgRequired(); 3291 3292 Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName); 3293 3294 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId, 3295 /* appStillExists = */ true); 3296 } 3297 3298 private void handleVerifyStates() throws CommandException { 3299 try { 3300 verifyStatesForce(); // This will throw when there's an issue. 3301 } catch (Throwable th) { 3302 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th)); 3303 } 3304 } 3305 } 3306 3307 // === Unit test support === 3308 3309 // Injection point. 3310 @VisibleForTesting 3311 long injectCurrentTimeMillis() { 3312 return System.currentTimeMillis(); 3313 } 3314 3315 @VisibleForTesting 3316 long injectElapsedRealtime() { 3317 return SystemClock.elapsedRealtime(); 3318 } 3319 3320 // Injection point. 3321 @VisibleForTesting 3322 int injectBinderCallingUid() { 3323 return getCallingUid(); 3324 } 3325 3326 private int getCallingUserId() { 3327 return UserHandle.getUserId(injectBinderCallingUid()); 3328 } 3329 3330 // Injection point. 3331 @VisibleForTesting 3332 long injectClearCallingIdentity() { 3333 return Binder.clearCallingIdentity(); 3334 } 3335 3336 // Injection point. 3337 @VisibleForTesting 3338 void injectRestoreCallingIdentity(long token) { 3339 Binder.restoreCallingIdentity(token); 3340 } 3341 3342 final void wtf(String message) { 3343 wtf(message, /* exception= */ null); 3344 } 3345 3346 // Injection point. 3347 void wtf(String message, Throwable e) { 3348 if (e == null) { 3349 e = new RuntimeException("Stacktrace"); 3350 } 3351 synchronized (mLock) { 3352 mWtfCount++; 3353 mLastWtfStacktrace = new Exception("Last failure was logged here:"); 3354 } 3355 Slog.wtf(TAG, message, e); 3356 } 3357 3358 @VisibleForTesting 3359 File injectSystemDataPath() { 3360 return Environment.getDataSystemDirectory(); 3361 } 3362 3363 @VisibleForTesting 3364 File injectUserDataPath(@UserIdInt int userId) { 3365 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); 3366 } 3367 3368 @VisibleForTesting 3369 boolean injectIsLowRamDevice() { 3370 return ActivityManager.isLowRamDeviceStatic(); 3371 } 3372 3373 @VisibleForTesting 3374 void injectRegisterUidObserver(IUidObserver observer, int which) { 3375 try { 3376 ActivityManagerNative.getDefault().registerUidObserver(observer, which); 3377 } catch (RemoteException shouldntHappen) { 3378 } 3379 } 3380 3381 @VisibleForTesting 3382 PackageManagerInternal injectPackageManagerInternal() { 3383 return mPackageManagerInternal; 3384 } 3385 3386 File getUserBitmapFilePath(@UserIdInt int userId) { 3387 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); 3388 } 3389 3390 @VisibleForTesting 3391 SparseArray<ShortcutUser> getShortcutsForTest() { 3392 return mUsers; 3393 } 3394 3395 @VisibleForTesting 3396 int getMaxShortcutsForTest() { 3397 return mMaxShortcuts; 3398 } 3399 3400 @VisibleForTesting 3401 int getMaxUpdatesPerIntervalForTest() { 3402 return mMaxUpdatesPerInterval; 3403 } 3404 3405 @VisibleForTesting 3406 long getResetIntervalForTest() { 3407 return mResetInterval; 3408 } 3409 3410 @VisibleForTesting 3411 int getMaxIconDimensionForTest() { 3412 return mMaxIconDimension; 3413 } 3414 3415 @VisibleForTesting 3416 CompressFormat getIconPersistFormatForTest() { 3417 return mIconPersistFormat; 3418 } 3419 3420 @VisibleForTesting 3421 int getIconPersistQualityForTest() { 3422 return mIconPersistQuality; 3423 } 3424 3425 @VisibleForTesting 3426 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) { 3427 synchronized (mLock) { 3428 final ShortcutUser user = mUsers.get(userId); 3429 if (user == null) return null; 3430 3431 return user.getAllPackagesForTest().get(packageName); 3432 } 3433 } 3434 3435 @VisibleForTesting 3436 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) { 3437 synchronized (mLock) { 3438 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId); 3439 if (pkg == null) return null; 3440 3441 return pkg.findShortcutById(shortcutId); 3442 } 3443 } 3444 3445 /** 3446 * Control whether {@link #verifyStates} should be performed. We always perform it during unit 3447 * tests. 3448 */ 3449 @VisibleForTesting 3450 boolean injectShouldPerformVerification() { 3451 return DEBUG; 3452 } 3453 3454 /** 3455 * Check various internal states and throws if there's any inconsistency. 3456 * This is normally only enabled during unit tests. 3457 */ 3458 final void verifyStates() { 3459 if (injectShouldPerformVerification()) { 3460 verifyStatesInner(); 3461 } 3462 } 3463 3464 private final void verifyStatesForce() { 3465 verifyStatesInner(); 3466 } 3467 3468 private void verifyStatesInner() { 3469 synchronized (this) { 3470 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); 3471 } 3472 } 3473} 3474