1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.pm; 17 18 import android.Manifest.permission; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.UserIdInt; 23 import android.app.ActivityManager; 24 import android.app.ActivityManagerInternal; 25 import android.app.AppGlobals; 26 import android.app.IUidObserver; 27 import android.app.usage.UsageStatsManagerInternal; 28 import android.appwidget.AppWidgetProviderInfo; 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.IntentSender; 35 import android.content.IntentSender.SendIntentException; 36 import android.content.pm.ActivityInfo; 37 import android.content.pm.ApplicationInfo; 38 import android.content.pm.IPackageManager; 39 import android.content.pm.IShortcutService; 40 import android.content.pm.LauncherApps; 41 import android.content.pm.LauncherApps.ShortcutQuery; 42 import android.content.pm.PackageInfo; 43 import android.content.pm.PackageManager; 44 import android.content.pm.PackageManagerInternal; 45 import android.content.pm.PackageManager.NameNotFoundException; 46 import android.content.pm.ParceledListSlice; 47 import android.content.pm.ResolveInfo; 48 import android.content.pm.ShortcutInfo; 49 import android.content.pm.ShortcutServiceInternal; 50 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 51 import android.content.res.Resources; 52 import android.content.res.XmlResourceParser; 53 import android.graphics.Bitmap; 54 import android.graphics.Bitmap.CompressFormat; 55 import android.graphics.Canvas; 56 import android.graphics.RectF; 57 import android.graphics.drawable.AdaptiveIconDrawable; 58 import android.graphics.drawable.Icon; 59 import android.net.Uri; 60 import android.os.Binder; 61 import android.os.Build; 62 import android.os.Bundle; 63 import android.os.Environment; 64 import android.os.FileUtils; 65 import android.os.Handler; 66 import android.os.LocaleList; 67 import android.os.Looper; 68 import android.os.ParcelFileDescriptor; 69 import android.os.PersistableBundle; 70 import android.os.Process; 71 import android.os.RemoteException; 72 import android.os.ResultReceiver; 73 import android.os.SELinux; 74 import android.os.ServiceManager; 75 import android.os.ShellCallback; 76 import android.os.ShellCommand; 77 import android.os.SystemClock; 78 import android.os.UserHandle; 79 import android.os.UserManagerInternal; 80 import android.text.TextUtils; 81 import android.text.format.Time; 82 import android.util.ArraySet; 83 import android.util.AtomicFile; 84 import android.util.KeyValueListParser; 85 import android.util.Log; 86 import android.util.Slog; 87 import android.util.SparseArray; 88 import android.util.SparseBooleanArray; 89 import android.util.SparseIntArray; 90 import android.util.SparseLongArray; 91 import android.util.TypedValue; 92 import android.util.Xml; 93 import android.view.IWindowManager; 94 95 import com.android.internal.annotations.GuardedBy; 96 import com.android.internal.annotations.VisibleForTesting; 97 import com.android.internal.os.BackgroundThread; 98 import com.android.internal.util.DumpUtils; 99 import com.android.internal.util.FastXmlSerializer; 100 import com.android.internal.util.Preconditions; 101 import com.android.server.LocalServices; 102 import com.android.internal.util.StatLogger; 103 import com.android.server.SystemService; 104 import com.android.server.pm.ShortcutUser.PackageWithUser; 105 106 import libcore.io.IoUtils; 107 108 import org.json.JSONArray; 109 import org.json.JSONException; 110 import org.json.JSONObject; 111 import org.xmlpull.v1.XmlPullParser; 112 import org.xmlpull.v1.XmlPullParserException; 113 import org.xmlpull.v1.XmlSerializer; 114 115 import java.io.BufferedInputStream; 116 import java.io.BufferedOutputStream; 117 import java.io.ByteArrayInputStream; 118 import java.io.ByteArrayOutputStream; 119 import java.io.File; 120 import java.io.FileDescriptor; 121 import java.io.FileInputStream; 122 import java.io.FileNotFoundException; 123 import java.io.FileOutputStream; 124 import java.io.IOException; 125 import java.io.InputStream; 126 import java.io.OutputStream; 127 import java.io.PrintWriter; 128 import java.lang.annotation.Retention; 129 import java.lang.annotation.RetentionPolicy; 130 import java.net.URISyntaxException; 131 import java.nio.charset.StandardCharsets; 132 import java.util.ArrayList; 133 import java.util.Collections; 134 import java.util.List; 135 import java.util.concurrent.atomic.AtomicBoolean; 136 import java.util.function.Consumer; 137 import java.util.function.Predicate; 138 import java.util.regex.Pattern; 139 140 /** 141 * TODO: 142 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi. 143 * -> But TypedValue.applyDimension() doesn't differentiate x and y..? 144 * 145 * - Detect when already registered instances are passed to APIs again, which might break 146 * internal bitmap handling. 147 */ 148 public class ShortcutService extends IShortcutService.Stub { 149 static final String TAG = "ShortcutService"; 150 151 static final boolean DEBUG = false; // STOPSHIP if true 152 static final boolean DEBUG_LOAD = false; // STOPSHIP if true 153 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true 154 155 @VisibleForTesting 156 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day 157 158 @VisibleForTesting 159 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10; 160 161 @VisibleForTesting 162 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5; 163 164 @VisibleForTesting 165 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; 166 167 @VisibleForTesting 168 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48; 169 170 @VisibleForTesting 171 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name(); 172 173 @VisibleForTesting 174 static final int DEFAULT_ICON_PERSIST_QUALITY = 100; 175 176 @VisibleForTesting 177 static final int DEFAULT_SAVE_DELAY_MS = 3000; 178 179 @VisibleForTesting 180 static final String FILENAME_BASE_STATE = "shortcut_service.xml"; 181 182 @VisibleForTesting 183 static final String DIRECTORY_PER_USER = "shortcut_service"; 184 185 @VisibleForTesting 186 static final String DIRECTORY_DUMP = "shortcut_dump"; 187 188 @VisibleForTesting 189 static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; 190 191 static final String DIRECTORY_BITMAPS = "bitmaps"; 192 193 private static final String TAG_ROOT = "root"; 194 private static final String TAG_LAST_RESET_TIME = "last_reset_time"; 195 196 private static final String ATTR_VALUE = "value"; 197 198 private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER; 199 200 private static final String KEY_SHORTCUT = "shortcut"; 201 private static final String KEY_LOW_RAM = "lowRam"; 202 private static final String KEY_ICON_SIZE = "iconSize"; 203 204 private static final String DUMMY_MAIN_ACTIVITY = "android.__dummy__"; 205 206 @VisibleForTesting 207 interface ConfigConstants { 208 /** 209 * Key name for the save delay, in milliseconds. (int) 210 */ 211 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms"; 212 213 /** 214 * Key name for the throttling reset interval, in seconds. (long) 215 */ 216 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec"; 217 218 /** 219 * Key name for the max number of modifying API calls per app for every interval. (int) 220 */ 221 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval"; 222 223 /** 224 * Key name for the max icon dimensions in DP, for non-low-memory devices. 225 */ 226 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp"; 227 228 /** 229 * Key name for the max icon dimensions in DP, for low-memory devices. 230 */ 231 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram"; 232 233 /** 234 * Key name for the max dynamic shortcuts per activity. (int) 235 */ 236 String KEY_MAX_SHORTCUTS = "max_shortcuts"; 237 238 /** 239 * Key name for icon compression quality, 0-100. 240 */ 241 String KEY_ICON_QUALITY = "icon_quality"; 242 243 /** 244 * Key name for icon compression format: "PNG", "JPEG" or "WEBP" 245 */ 246 String KEY_ICON_FORMAT = "icon_format"; 247 } 248 249 final Context mContext; 250 251 private final Object mLock = new Object(); 252 253 private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0); 254 255 // Temporarily reverted to anonymous inner class form due to: b/32554459 256 private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = new Predicate<ResolveInfo>() { 257 public boolean test(ResolveInfo ri) { 258 return !ri.activityInfo.exported; 259 } 260 }; 261 262 // Temporarily reverted to anonymous inner class form due to: b/32554459 263 private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = new Predicate<PackageInfo>() { 264 public boolean test(PackageInfo pi) { 265 return !isInstalled(pi); 266 } 267 }; 268 269 private final Handler mHandler; 270 271 @GuardedBy("mLock") 272 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); 273 274 @GuardedBy("mLock") 275 private long mRawLastResetTime; 276 277 /** 278 * User ID -> UserShortcuts 279 */ 280 @GuardedBy("mLock") 281 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>(); 282 283 /** 284 * User ID -> ShortcutNonPersistentUser 285 */ 286 @GuardedBy("mLock") 287 private final SparseArray<ShortcutNonPersistentUser> mShortcutNonPersistentUsers = 288 new SparseArray<>(); 289 290 /** 291 * Max number of dynamic + manifest shortcuts that each application can have at a time. 292 */ 293 private int mMaxShortcuts; 294 295 /** 296 * Max number of updating API calls that each application can make during the interval. 297 */ 298 int mMaxUpdatesPerInterval; 299 300 /** 301 * Actual throttling-reset interval. By default it's a day. 302 */ 303 private long mResetInterval; 304 305 /** 306 * Icon max width/height in pixels. 307 */ 308 private int mMaxIconDimension; 309 310 private CompressFormat mIconPersistFormat; 311 private int mIconPersistQuality; 312 313 private int mSaveDelayMillis; 314 315 private final IPackageManager mIPackageManager; 316 private final PackageManagerInternal mPackageManagerInternal; 317 private final UserManagerInternal mUserManagerInternal; 318 private final UsageStatsManagerInternal mUsageStatsManagerInternal; 319 private final ActivityManagerInternal mActivityManagerInternal; 320 321 private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor; 322 private final ShortcutBitmapSaver mShortcutBitmapSaver; 323 private final ShortcutDumpFiles mShortcutDumpFiles; 324 325 @GuardedBy("mLock") 326 final SparseIntArray mUidState = new SparseIntArray(); 327 328 @GuardedBy("mLock") 329 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray(); 330 331 @GuardedBy("mLock") 332 private List<Integer> mDirtyUserIds = new ArrayList<>(); 333 334 private final AtomicBoolean mBootCompleted = new AtomicBoolean(); 335 336 private static final int PACKAGE_MATCH_FLAGS = 337 PackageManager.MATCH_DIRECT_BOOT_AWARE 338 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 339 | PackageManager.MATCH_UNINSTALLED_PACKAGES; 340 341 /** 342 * Note we use a fine-grained lock for {@link #mUnlockedUsers} due to b/64303666. 343 */ 344 @GuardedBy("mUnlockedUsers") 345 final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray(); 346 347 // Stats 348 @VisibleForTesting 349 interface Stats { 350 int GET_DEFAULT_HOME = 0; 351 int GET_PACKAGE_INFO = 1; 352 int GET_PACKAGE_INFO_WITH_SIG = 2; 353 int GET_APPLICATION_INFO = 3; 354 int LAUNCHER_PERMISSION_CHECK = 4; 355 int CLEANUP_DANGLING_BITMAPS = 5; 356 int GET_ACTIVITY_WITH_METADATA = 6; 357 int GET_INSTALLED_PACKAGES = 7; 358 int CHECK_PACKAGE_CHANGES = 8; 359 int GET_APPLICATION_RESOURCES = 9; 360 int RESOURCE_NAME_LOOKUP = 10; 361 int GET_LAUNCHER_ACTIVITY = 11; 362 int CHECK_LAUNCHER_ACTIVITY = 12; 363 int IS_ACTIVITY_ENABLED = 13; 364 int PACKAGE_UPDATE_CHECK = 14; 365 int ASYNC_PRELOAD_USER_DELAY = 15; 366 int GET_DEFAULT_LAUNCHER = 16; 367 368 int COUNT = GET_DEFAULT_LAUNCHER + 1; 369 } 370 371 private final StatLogger mStatLogger = new StatLogger(new String[] { 372 "getHomeActivities()", 373 "Launcher permission check", 374 "getPackageInfo()", 375 "getPackageInfo(SIG)", 376 "getApplicationInfo", 377 "cleanupDanglingBitmaps", 378 "getActivity+metadata", 379 "getInstalledPackages", 380 "checkPackageChanges", 381 "getApplicationResources", 382 "resourceNameLookup", 383 "getLauncherActivity", 384 "checkLauncherActivity", 385 "isActivityEnabled", 386 "packageUpdateCheck", 387 "asyncPreloadUserDelay", 388 "getDefaultLauncher()" 389 }); 390 391 private static final int PROCESS_STATE_FOREGROUND_THRESHOLD = 392 ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; 393 394 static final int OPERATION_SET = 0; 395 static final int OPERATION_ADD = 1; 396 static final int OPERATION_UPDATE = 2; 397 398 /** @hide */ 399 @IntDef(value = { 400 OPERATION_SET, 401 OPERATION_ADD, 402 OPERATION_UPDATE 403 }) 404 @Retention(RetentionPolicy.SOURCE) 405 @interface ShortcutOperation { 406 } 407 408 @GuardedBy("mLock") 409 private int mWtfCount = 0; 410 411 @GuardedBy("mLock") 412 private Exception mLastWtfStacktrace; 413 414 static class InvalidFileFormatException extends Exception { InvalidFileFormatException(String message, Throwable cause)415 public InvalidFileFormatException(String message, Throwable cause) { 416 super(message, cause); 417 } 418 } 419 ShortcutService(Context context)420 public ShortcutService(Context context) { 421 this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); 422 } 423 424 @VisibleForTesting ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis)425 ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) { 426 mContext = Preconditions.checkNotNull(context); 427 LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); 428 mHandler = new Handler(looper); 429 mIPackageManager = AppGlobals.getPackageManager(); 430 mPackageManagerInternal = Preconditions.checkNotNull( 431 LocalServices.getService(PackageManagerInternal.class)); 432 mUserManagerInternal = Preconditions.checkNotNull( 433 LocalServices.getService(UserManagerInternal.class)); 434 mUsageStatsManagerInternal = Preconditions.checkNotNull( 435 LocalServices.getService(UsageStatsManagerInternal.class)); 436 mActivityManagerInternal = Preconditions.checkNotNull( 437 LocalServices.getService(ActivityManagerInternal.class)); 438 439 mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock); 440 mShortcutBitmapSaver = new ShortcutBitmapSaver(this); 441 mShortcutDumpFiles = new ShortcutDumpFiles(this); 442 443 if (onlyForPackageManagerApis) { 444 return; // Don't do anything further. For unit tests only. 445 } 446 447 // Register receivers. 448 449 // We need to set a priority, so let's just not use PackageMonitor for now. 450 // TODO Refactor PackageMonitor to support priorities. 451 final IntentFilter packageFilter = new IntentFilter(); 452 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 453 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 454 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 455 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 456 packageFilter.addDataScheme("package"); 457 packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 458 mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, 459 packageFilter, null, mHandler); 460 461 final IntentFilter preferedActivityFilter = new IntentFilter(); 462 preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); 463 preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 464 mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, 465 preferedActivityFilter, null, mHandler); 466 467 final IntentFilter localeFilter = new IntentFilter(); 468 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED); 469 localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 470 mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, 471 localeFilter, null, mHandler); 472 473 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE 474 | ActivityManager.UID_OBSERVER_GONE); 475 } 476 getStatStartTime()477 long getStatStartTime() { 478 return mStatLogger.getTime(); 479 } 480 logDurationStat(int statId, long start)481 void logDurationStat(int statId, long start) { 482 mStatLogger.logDurationStat(statId, start); 483 } 484 injectGetLocaleTagsForUser(@serIdInt int userId)485 public String injectGetLocaleTagsForUser(@UserIdInt int userId) { 486 // TODO This should get the per-user locale. b/30123329 b/30119489 487 return LocaleList.getDefault().toLanguageTags(); 488 } 489 490 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 491 @Override 492 public void onUidStateChanged(int uid, int procState, long procStateSeq) { 493 injectPostToHandler(() -> handleOnUidStateChanged(uid, procState)); 494 } 495 496 @Override 497 public void onUidGone(int uid, boolean disabled) { 498 injectPostToHandler(() -> 499 handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT)); 500 } 501 502 @Override 503 public void onUidActive(int uid) { 504 } 505 506 @Override 507 public void onUidIdle(int uid, boolean disabled) { 508 } 509 510 @Override public void onUidCachedChanged(int uid, boolean cached) { 511 } 512 }; 513 handleOnUidStateChanged(int uid, int procState)514 void handleOnUidStateChanged(int uid, int procState) { 515 if (DEBUG_PROCSTATE) { 516 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState); 517 } 518 synchronized (mLock) { 519 mUidState.put(uid, procState); 520 521 // We need to keep track of last time an app comes to foreground. 522 // See ShortcutPackage.getApiCallCount() for how it's used. 523 // It doesn't have to be persisted, but it needs to be the elapsed time. 524 if (isProcessStateForeground(procState)) { 525 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime()); 526 } 527 } 528 } 529 isProcessStateForeground(int processState)530 private boolean isProcessStateForeground(int processState) { 531 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD; 532 } 533 534 @GuardedBy("mLock") isUidForegroundLocked(int uid)535 boolean isUidForegroundLocked(int uid) { 536 if (uid == Process.SYSTEM_UID) { 537 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services, 538 // so it's foreground anyway. 539 return true; 540 } 541 // First, check with the local cache. 542 if (isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE))) { 543 return true; 544 } 545 // If the cache says background, reach out to AM. Since it'll internally need to hold 546 // the AM lock, we use it as a last resort. 547 return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid)); 548 } 549 550 @GuardedBy("mLock") getUidLastForegroundElapsedTimeLocked(int uid)551 long getUidLastForegroundElapsedTimeLocked(int uid) { 552 return mUidLastForegroundElapsedTime.get(uid); 553 } 554 555 /** 556 * System service lifecycle. 557 */ 558 public static final class Lifecycle extends SystemService { 559 final ShortcutService mService; 560 Lifecycle(Context context)561 public Lifecycle(Context context) { 562 super(context); 563 if (DEBUG) { 564 Binder.LOG_RUNTIME_EXCEPTION = true; 565 } 566 mService = new ShortcutService(context); 567 } 568 569 @Override onStart()570 public void onStart() { 571 publishBinderService(Context.SHORTCUT_SERVICE, mService); 572 } 573 574 @Override onBootPhase(int phase)575 public void onBootPhase(int phase) { 576 mService.onBootPhase(phase); 577 } 578 579 @Override onStopUser(int userHandle)580 public void onStopUser(int userHandle) { 581 mService.handleStopUser(userHandle); 582 } 583 584 @Override onUnlockUser(int userId)585 public void onUnlockUser(int userId) { 586 mService.handleUnlockUser(userId); 587 } 588 } 589 590 /** lifecycle event */ onBootPhase(int phase)591 void onBootPhase(int phase) { 592 if (DEBUG) { 593 Slog.d(TAG, "onBootPhase: " + phase); 594 } 595 switch (phase) { 596 case SystemService.PHASE_LOCK_SETTINGS_READY: 597 initialize(); 598 break; 599 case SystemService.PHASE_BOOT_COMPLETED: 600 mBootCompleted.set(true); 601 break; 602 } 603 } 604 605 /** lifecycle event */ handleUnlockUser(int userId)606 void handleUnlockUser(int userId) { 607 if (DEBUG) { 608 Slog.d(TAG, "handleUnlockUser: user=" + userId); 609 } 610 synchronized (mUnlockedUsers) { 611 mUnlockedUsers.put(userId, true); 612 } 613 614 // Preload the user data. 615 // Note, we don't use mHandler here but instead just start a new thread. 616 // This is because mHandler (which uses com.android.internal.os.BackgroundThread) is very 617 // busy at this point and this could take hundreds of milliseconds, which would be too 618 // late since the launcher would already have started. 619 // So we just create a new thread. This code runs rarely, so we don't use a thread pool 620 // or anything. 621 final long start = getStatStartTime(); 622 injectRunOnNewThread(() -> { 623 synchronized (mLock) { 624 logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start); 625 getUserShortcutsLocked(userId); 626 } 627 }); 628 } 629 630 /** lifecycle event */ handleStopUser(int userId)631 void handleStopUser(int userId) { 632 if (DEBUG) { 633 Slog.d(TAG, "handleStopUser: user=" + userId); 634 } 635 synchronized (mLock) { 636 unloadUserLocked(userId); 637 638 synchronized (mUnlockedUsers) { 639 mUnlockedUsers.put(userId, false); 640 } 641 } 642 } 643 644 @GuardedBy("mLock") unloadUserLocked(int userId)645 private void unloadUserLocked(int userId) { 646 if (DEBUG) { 647 Slog.d(TAG, "unloadUserLocked: user=" + userId); 648 } 649 // Save all dirty information. 650 saveDirtyInfo(); 651 652 // Unload 653 mUsers.delete(userId); 654 } 655 656 /** Return the base state file name */ getBaseStateFile()657 private AtomicFile getBaseStateFile() { 658 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE); 659 path.mkdirs(); 660 return new AtomicFile(path); 661 } 662 663 /** 664 * Init the instance. (load the state file, etc) 665 */ initialize()666 private void initialize() { 667 synchronized (mLock) { 668 loadConfigurationLocked(); 669 loadBaseStateLocked(); 670 } 671 } 672 673 /** 674 * Load the configuration from Settings. 675 */ loadConfigurationLocked()676 private void loadConfigurationLocked() { 677 updateConfigurationLocked(injectShortcutManagerConstants()); 678 } 679 680 /** 681 * Load the configuration from Settings. 682 */ 683 @VisibleForTesting updateConfigurationLocked(String config)684 boolean updateConfigurationLocked(String config) { 685 boolean result = true; 686 687 final KeyValueListParser parser = new KeyValueListParser(','); 688 try { 689 parser.setString(config); 690 } catch (IllegalArgumentException e) { 691 // Failed to parse the settings string, log this and move on 692 // with defaults. 693 Slog.e(TAG, "Bad shortcut manager settings", e); 694 result = false; 695 } 696 697 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS, 698 DEFAULT_SAVE_DELAY_MS)); 699 700 mResetInterval = Math.max(1, parser.getLong( 701 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC) 702 * 1000L); 703 704 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong( 705 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL)); 706 707 mMaxShortcuts = Math.max(0, (int) parser.getLong( 708 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP)); 709 710 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() 711 ? (int) parser.getLong( 712 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, 713 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP) 714 : (int) parser.getLong( 715 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP, 716 DEFAULT_MAX_ICON_DIMENSION_DP)); 717 718 mMaxIconDimension = injectDipToPixel(iconDimensionDp); 719 720 mIconPersistFormat = CompressFormat.valueOf( 721 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT)); 722 723 mIconPersistQuality = (int) parser.getLong( 724 ConfigConstants.KEY_ICON_QUALITY, 725 DEFAULT_ICON_PERSIST_QUALITY); 726 727 return result; 728 } 729 730 @VisibleForTesting injectShortcutManagerConstants()731 String injectShortcutManagerConstants() { 732 return android.provider.Settings.Global.getString( 733 mContext.getContentResolver(), 734 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS); 735 } 736 737 @VisibleForTesting injectDipToPixel(int dip)738 int injectDipToPixel(int dip) { 739 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 740 mContext.getResources().getDisplayMetrics()); 741 } 742 743 // === Persisting === 744 745 @Nullable parseStringAttribute(XmlPullParser parser, String attribute)746 static String parseStringAttribute(XmlPullParser parser, String attribute) { 747 return parser.getAttributeValue(null, attribute); 748 } 749 parseBooleanAttribute(XmlPullParser parser, String attribute)750 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) { 751 return parseLongAttribute(parser, attribute) == 1; 752 } 753 parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def)754 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) { 755 return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1; 756 } 757 parseIntAttribute(XmlPullParser parser, String attribute)758 static int parseIntAttribute(XmlPullParser parser, String attribute) { 759 return (int) parseLongAttribute(parser, attribute); 760 } 761 parseIntAttribute(XmlPullParser parser, String attribute, int def)762 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) { 763 return (int) parseLongAttribute(parser, attribute, def); 764 } 765 parseLongAttribute(XmlPullParser parser, String attribute)766 static long parseLongAttribute(XmlPullParser parser, String attribute) { 767 return parseLongAttribute(parser, attribute, 0); 768 } 769 parseLongAttribute(XmlPullParser parser, String attribute, long def)770 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) { 771 final String value = parseStringAttribute(parser, attribute); 772 if (TextUtils.isEmpty(value)) { 773 return def; 774 } 775 try { 776 return Long.parseLong(value); 777 } catch (NumberFormatException e) { 778 Slog.e(TAG, "Error parsing long " + value); 779 return def; 780 } 781 } 782 783 @Nullable parseComponentNameAttribute(XmlPullParser parser, String attribute)784 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { 785 final String value = parseStringAttribute(parser, attribute); 786 if (TextUtils.isEmpty(value)) { 787 return null; 788 } 789 return ComponentName.unflattenFromString(value); 790 } 791 792 @Nullable parseIntentAttributeNoDefault(XmlPullParser parser, String attribute)793 static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) { 794 final String value = parseStringAttribute(parser, attribute); 795 Intent parsed = null; 796 if (!TextUtils.isEmpty(value)) { 797 try { 798 parsed = Intent.parseUri(value, /* flags =*/ 0); 799 } catch (URISyntaxException e) { 800 Slog.e(TAG, "Error parsing intent", e); 801 } 802 } 803 return parsed; 804 } 805 806 @Nullable parseIntentAttribute(XmlPullParser parser, String attribute)807 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { 808 Intent parsed = parseIntentAttributeNoDefault(parser, attribute); 809 if (parsed == null) { 810 // Default intent. 811 parsed = new Intent(Intent.ACTION_VIEW); 812 } 813 return parsed; 814 } 815 writeTagValue(XmlSerializer out, String tag, String value)816 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { 817 if (TextUtils.isEmpty(value)) return; 818 819 out.startTag(null, tag); 820 out.attribute(null, ATTR_VALUE, value); 821 out.endTag(null, tag); 822 } 823 writeTagValue(XmlSerializer out, String tag, long value)824 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { 825 writeTagValue(out, tag, Long.toString(value)); 826 } 827 writeTagValue(XmlSerializer out, String tag, ComponentName name)828 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { 829 if (name == null) return; 830 writeTagValue(out, tag, name.flattenToString()); 831 } 832 writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)833 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) 834 throws IOException, XmlPullParserException { 835 if (bundle == null) return; 836 837 out.startTag(null, tag); 838 bundle.saveToXml(out); 839 out.endTag(null, tag); 840 } 841 writeAttr(XmlSerializer out, String name, CharSequence value)842 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException { 843 if (TextUtils.isEmpty(value)) return; 844 845 out.attribute(null, name, value.toString()); 846 } 847 writeAttr(XmlSerializer out, String name, long value)848 static void writeAttr(XmlSerializer out, String name, long value) throws IOException { 849 writeAttr(out, name, String.valueOf(value)); 850 } 851 writeAttr(XmlSerializer out, String name, boolean value)852 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException { 853 if (value) { 854 writeAttr(out, name, "1"); 855 } else { 856 writeAttr(out, name, "0"); 857 } 858 } 859 writeAttr(XmlSerializer out, String name, ComponentName comp)860 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { 861 if (comp == null) return; 862 writeAttr(out, name, comp.flattenToString()); 863 } 864 writeAttr(XmlSerializer out, String name, Intent intent)865 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { 866 if (intent == null) return; 867 868 writeAttr(out, name, intent.toUri(/* flags =*/ 0)); 869 } 870 871 @GuardedBy("mLock") 872 @VisibleForTesting saveBaseStateLocked()873 void saveBaseStateLocked() { 874 final AtomicFile file = getBaseStateFile(); 875 if (DEBUG) { 876 Slog.d(TAG, "Saving to " + file.getBaseFile()); 877 } 878 879 FileOutputStream outs = null; 880 try { 881 outs = file.startWrite(); 882 883 // Write to XML 884 XmlSerializer out = new FastXmlSerializer(); 885 out.setOutput(outs, StandardCharsets.UTF_8.name()); 886 out.startDocument(null, true); 887 out.startTag(null, TAG_ROOT); 888 889 // Body. 890 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime); 891 892 // Epilogue. 893 out.endTag(null, TAG_ROOT); 894 out.endDocument(); 895 896 // Close. 897 file.finishWrite(outs); 898 } catch (IOException e) { 899 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 900 file.failWrite(outs); 901 } 902 } 903 904 @GuardedBy("mLock") loadBaseStateLocked()905 private void loadBaseStateLocked() { 906 mRawLastResetTime = 0; 907 908 final AtomicFile file = getBaseStateFile(); 909 if (DEBUG) { 910 Slog.d(TAG, "Loading from " + file.getBaseFile()); 911 } 912 try (FileInputStream in = file.openRead()) { 913 XmlPullParser parser = Xml.newPullParser(); 914 parser.setInput(in, StandardCharsets.UTF_8.name()); 915 916 int type; 917 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 918 if (type != XmlPullParser.START_TAG) { 919 continue; 920 } 921 final int depth = parser.getDepth(); 922 // Check the root tag 923 final String tag = parser.getName(); 924 if (depth == 1) { 925 if (!TAG_ROOT.equals(tag)) { 926 Slog.e(TAG, "Invalid root tag: " + tag); 927 return; 928 } 929 continue; 930 } 931 // Assume depth == 2 932 switch (tag) { 933 case TAG_LAST_RESET_TIME: 934 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE); 935 break; 936 default: 937 Slog.e(TAG, "Invalid tag: " + tag); 938 break; 939 } 940 } 941 } catch (FileNotFoundException e) { 942 // Use the default 943 } catch (IOException | XmlPullParserException e) { 944 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 945 946 mRawLastResetTime = 0; 947 } 948 // Adjust the last reset time. 949 getLastResetTimeLocked(); 950 } 951 952 @VisibleForTesting getUserFile(@serIdInt int userId)953 final File getUserFile(@UserIdInt int userId) { 954 return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 955 } 956 957 @GuardedBy("mLock") saveUserLocked(@serIdInt int userId)958 private void saveUserLocked(@UserIdInt int userId) { 959 final File path = getUserFile(userId); 960 if (DEBUG) { 961 Slog.d(TAG, "Saving to " + path); 962 } 963 964 mShortcutBitmapSaver.waitForAllSavesLocked(); 965 966 path.getParentFile().mkdirs(); 967 final AtomicFile file = new AtomicFile(path); 968 FileOutputStream os = null; 969 try { 970 os = file.startWrite(); 971 972 saveUserInternalLocked(userId, os, /* forBackup= */ false); 973 974 file.finishWrite(os); 975 976 // Remove all dangling bitmap files. 977 cleanupDanglingBitmapDirectoriesLocked(userId); 978 } catch (XmlPullParserException | IOException e) { 979 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 980 file.failWrite(os); 981 } 982 } 983 984 @GuardedBy("mLock") saveUserInternalLocked(@serIdInt int userId, OutputStream os, boolean forBackup)985 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, 986 boolean forBackup) throws IOException, XmlPullParserException { 987 988 final BufferedOutputStream bos = new BufferedOutputStream(os); 989 990 // Write to XML 991 XmlSerializer out = new FastXmlSerializer(); 992 out.setOutput(bos, StandardCharsets.UTF_8.name()); 993 out.startDocument(null, true); 994 995 getUserShortcutsLocked(userId).saveToXml(out, forBackup); 996 997 out.endDocument(); 998 999 bos.flush(); 1000 os.flush(); 1001 } 1002 throwForInvalidTag(int depth, String tag)1003 static IOException throwForInvalidTag(int depth, String tag) throws IOException { 1004 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth)); 1005 } 1006 warnForInvalidTag(int depth, String tag)1007 static void warnForInvalidTag(int depth, String tag) throws IOException { 1008 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); 1009 } 1010 1011 @Nullable loadUserLocked(@serIdInt int userId)1012 private ShortcutUser loadUserLocked(@UserIdInt int userId) { 1013 final File path = getUserFile(userId); 1014 if (DEBUG) { 1015 Slog.d(TAG, "Loading from " + path); 1016 } 1017 final AtomicFile file = new AtomicFile(path); 1018 1019 final FileInputStream in; 1020 try { 1021 in = file.openRead(); 1022 } catch (FileNotFoundException e) { 1023 if (DEBUG) { 1024 Slog.d(TAG, "Not found " + path); 1025 } 1026 return null; 1027 } 1028 try { 1029 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false); 1030 return ret; 1031 } catch (IOException | XmlPullParserException | InvalidFileFormatException e) { 1032 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 1033 return null; 1034 } finally { 1035 IoUtils.closeQuietly(in); 1036 } 1037 } 1038 loadUserInternal(@serIdInt int userId, InputStream is, boolean fromBackup)1039 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, 1040 boolean fromBackup) throws XmlPullParserException, IOException, 1041 InvalidFileFormatException { 1042 1043 final BufferedInputStream bis = new BufferedInputStream(is); 1044 1045 ShortcutUser ret = null; 1046 XmlPullParser parser = Xml.newPullParser(); 1047 parser.setInput(bis, StandardCharsets.UTF_8.name()); 1048 1049 int type; 1050 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 1051 if (type != XmlPullParser.START_TAG) { 1052 continue; 1053 } 1054 final int depth = parser.getDepth(); 1055 1056 final String tag = parser.getName(); 1057 if (DEBUG_LOAD) { 1058 Slog.d(TAG, String.format("depth=%d type=%d name=%s", 1059 depth, type, tag)); 1060 } 1061 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) { 1062 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup); 1063 continue; 1064 } 1065 throwForInvalidTag(depth, tag); 1066 } 1067 return ret; 1068 } 1069 scheduleSaveBaseState()1070 private void scheduleSaveBaseState() { 1071 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. 1072 } 1073 scheduleSaveUser(@serIdInt int userId)1074 void scheduleSaveUser(@UserIdInt int userId) { 1075 scheduleSaveInner(userId); 1076 } 1077 1078 // In order to re-schedule, we need to reuse the same instance, so keep it in final. 1079 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo; 1080 scheduleSaveInner(@serIdInt int userId)1081 private void scheduleSaveInner(@UserIdInt int userId) { 1082 if (DEBUG) { 1083 Slog.d(TAG, "Scheduling to save for " + userId); 1084 } 1085 synchronized (mLock) { 1086 if (!mDirtyUserIds.contains(userId)) { 1087 mDirtyUserIds.add(userId); 1088 } 1089 } 1090 // If already scheduled, remove that and re-schedule in N seconds. 1091 mHandler.removeCallbacks(mSaveDirtyInfoRunner); 1092 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis); 1093 } 1094 1095 @VisibleForTesting saveDirtyInfo()1096 void saveDirtyInfo() { 1097 if (DEBUG) { 1098 Slog.d(TAG, "saveDirtyInfo"); 1099 } 1100 try { 1101 synchronized (mLock) { 1102 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) { 1103 final int userId = mDirtyUserIds.get(i); 1104 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. 1105 saveBaseStateLocked(); 1106 } else { 1107 saveUserLocked(userId); 1108 } 1109 } 1110 mDirtyUserIds.clear(); 1111 } 1112 } catch (Exception e) { 1113 wtf("Exception in saveDirtyInfo", e); 1114 } 1115 } 1116 1117 /** Return the last reset time. */ 1118 @GuardedBy("mLock") getLastResetTimeLocked()1119 long getLastResetTimeLocked() { 1120 updateTimesLocked(); 1121 return mRawLastResetTime; 1122 } 1123 1124 /** Return the next reset time. */ 1125 @GuardedBy("mLock") getNextResetTimeLocked()1126 long getNextResetTimeLocked() { 1127 updateTimesLocked(); 1128 return mRawLastResetTime + mResetInterval; 1129 } 1130 isClockValid(long time)1131 static boolean isClockValid(long time) { 1132 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT 1133 } 1134 1135 /** 1136 * Update the last reset time. 1137 */ 1138 @GuardedBy("mLock") updateTimesLocked()1139 private void updateTimesLocked() { 1140 1141 final long now = injectCurrentTimeMillis(); 1142 1143 final long prevLastResetTime = mRawLastResetTime; 1144 1145 if (mRawLastResetTime == 0) { // first launch. 1146 // TODO Randomize?? 1147 mRawLastResetTime = now; 1148 } else if (now < mRawLastResetTime) { 1149 // Clock rewound. 1150 if (isClockValid(now)) { 1151 Slog.w(TAG, "Clock rewound"); 1152 // TODO Randomize?? 1153 mRawLastResetTime = now; 1154 } 1155 } else { 1156 if ((mRawLastResetTime + mResetInterval) <= now) { 1157 final long offset = mRawLastResetTime % mResetInterval; 1158 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset; 1159 } 1160 } 1161 if (prevLastResetTime != mRawLastResetTime) { 1162 scheduleSaveBaseState(); 1163 } 1164 } 1165 1166 // Requires mLock held, but "Locked" prefix would look weired so we just say "L". isUserUnlockedL(@serIdInt int userId)1167 protected boolean isUserUnlockedL(@UserIdInt int userId) { 1168 // First, check the local copy. 1169 synchronized (mUnlockedUsers) { 1170 if (mUnlockedUsers.get(userId)) { 1171 return true; 1172 } 1173 } 1174 1175 // If the local copy says the user is locked, check with AM for the actual state, since 1176 // the user might just have been unlocked. 1177 // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false 1178 // when the user is STOPPING, which we still want to consider as "unlocked". 1179 return mUserManagerInternal.isUserUnlockingOrUnlocked(userId); 1180 } 1181 1182 // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L". throwIfUserLockedL(@serIdInt int userId)1183 void throwIfUserLockedL(@UserIdInt int userId) { 1184 if (!isUserUnlockedL(userId)) { 1185 throw new IllegalStateException("User " + userId + " is locked or not running"); 1186 } 1187 } 1188 1189 @GuardedBy("mLock") 1190 @NonNull isUserLoadedLocked(@serIdInt int userId)1191 private boolean isUserLoadedLocked(@UserIdInt int userId) { 1192 return mUsers.get(userId) != null; 1193 } 1194 1195 /** Return the per-user state. */ 1196 @GuardedBy("mLock") 1197 @NonNull getUserShortcutsLocked(@serIdInt int userId)1198 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { 1199 if (!isUserUnlockedL(userId)) { 1200 wtf("User still locked"); 1201 } 1202 1203 ShortcutUser userPackages = mUsers.get(userId); 1204 if (userPackages == null) { 1205 userPackages = loadUserLocked(userId); 1206 if (userPackages == null) { 1207 userPackages = new ShortcutUser(this, userId); 1208 } 1209 mUsers.put(userId, userPackages); 1210 1211 // Also when a user's data is first accessed, scan all packages. 1212 checkPackageChanges(userId); 1213 } 1214 return userPackages; 1215 } 1216 1217 /** Return the non-persistent per-user state. */ 1218 @GuardedBy("mLock") 1219 @NonNull getNonPersistentUserLocked(@serIdInt int userId)1220 ShortcutNonPersistentUser getNonPersistentUserLocked(@UserIdInt int userId) { 1221 ShortcutNonPersistentUser ret = mShortcutNonPersistentUsers.get(userId); 1222 if (ret == null) { 1223 ret = new ShortcutNonPersistentUser(this, userId); 1224 mShortcutNonPersistentUsers.put(userId, ret); 1225 } 1226 return ret; 1227 } 1228 1229 @GuardedBy("mLock") forEachLoadedUserLocked(@onNull Consumer<ShortcutUser> c)1230 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) { 1231 for (int i = mUsers.size() - 1; i >= 0; i--) { 1232 c.accept(mUsers.valueAt(i)); 1233 } 1234 } 1235 1236 /** 1237 * Return the per-user per-package state. If the caller is a publisher, use 1238 * {@link #getPackageShortcutsForPublisherLocked} instead. 1239 */ 1240 @GuardedBy("mLock") 1241 @NonNull getPackageShortcutsLocked( @onNull String packageName, @UserIdInt int userId)1242 ShortcutPackage getPackageShortcutsLocked( 1243 @NonNull String packageName, @UserIdInt int userId) { 1244 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName); 1245 } 1246 1247 /** Return the per-user per-package state. Use this when the caller is a publisher. */ 1248 @GuardedBy("mLock") 1249 @NonNull getPackageShortcutsForPublisherLocked( @onNull String packageName, @UserIdInt int userId)1250 ShortcutPackage getPackageShortcutsForPublisherLocked( 1251 @NonNull String packageName, @UserIdInt int userId) { 1252 final ShortcutPackage ret = getUserShortcutsLocked(userId).getPackageShortcuts(packageName); 1253 ret.getUser().onCalledByPublisher(packageName); 1254 return ret; 1255 } 1256 1257 @GuardedBy("mLock") 1258 @NonNull getLauncherShortcutsLocked( @onNull String packageName, @UserIdInt int ownerUserId, @UserIdInt int launcherUserId)1259 ShortcutLauncher getLauncherShortcutsLocked( 1260 @NonNull String packageName, @UserIdInt int ownerUserId, 1261 @UserIdInt int launcherUserId) { 1262 return getUserShortcutsLocked(ownerUserId) 1263 .getLauncherShortcuts(packageName, launcherUserId); 1264 } 1265 1266 // === Caller validation === 1267 removeIconLocked(ShortcutInfo shortcut)1268 void removeIconLocked(ShortcutInfo shortcut) { 1269 mShortcutBitmapSaver.removeIcon(shortcut); 1270 } 1271 cleanupBitmapsForPackage(@serIdInt int userId, String packageName)1272 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) { 1273 final File packagePath = new File(getUserBitmapFilePath(userId), packageName); 1274 if (!packagePath.isDirectory()) { 1275 return; 1276 } 1277 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) { 1278 Slog.w(TAG, "Unable to remove directory " + packagePath); 1279 } 1280 } 1281 1282 /** 1283 * Remove dangling bitmap files for a user. 1284 * 1285 * Note this method must be called with the lock held after calling 1286 * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap 1287 * saves are going on. 1288 */ 1289 @GuardedBy("mLock") cleanupDanglingBitmapDirectoriesLocked(@serIdInt int userId)1290 private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) { 1291 if (DEBUG) { 1292 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); 1293 } 1294 final long start = getStatStartTime(); 1295 1296 final ShortcutUser user = getUserShortcutsLocked(userId); 1297 1298 final File bitmapDir = getUserBitmapFilePath(userId); 1299 final File[] children = bitmapDir.listFiles(); 1300 if (children == null) { 1301 return; 1302 } 1303 for (File child : children) { 1304 if (!child.isDirectory()) { 1305 continue; 1306 } 1307 final String packageName = child.getName(); 1308 if (DEBUG) { 1309 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName); 1310 } 1311 if (!user.hasPackage(packageName)) { 1312 if (DEBUG) { 1313 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName); 1314 } 1315 cleanupBitmapsForPackage(userId, packageName); 1316 } else { 1317 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child); 1318 } 1319 } 1320 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start); 1321 } 1322 1323 /** 1324 * Remove dangling bitmap files for a package. 1325 * 1326 * Note this method must be called with the lock held after calling 1327 * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap 1328 * saves are going on. 1329 */ cleanupDanglingBitmapFilesLocked(@serIdInt int userId, @NonNull ShortcutUser user, @NonNull String packageName, @NonNull File path)1330 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user, 1331 @NonNull String packageName, @NonNull File path) { 1332 final ArraySet<String> usedFiles = 1333 user.getPackageShortcuts(packageName).getUsedBitmapFiles(); 1334 1335 for (File child : path.listFiles()) { 1336 if (!child.isFile()) { 1337 continue; 1338 } 1339 final String name = child.getName(); 1340 if (!usedFiles.contains(name)) { 1341 if (DEBUG) { 1342 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath()); 1343 } 1344 child.delete(); 1345 } 1346 } 1347 } 1348 1349 @VisibleForTesting 1350 static class FileOutputStreamWithPath extends FileOutputStream { 1351 private final File mFile; 1352 FileOutputStreamWithPath(File file)1353 public FileOutputStreamWithPath(File file) throws FileNotFoundException { 1354 super(file); 1355 mFile = file; 1356 } 1357 getFile()1358 public File getFile() { 1359 return mFile; 1360 } 1361 } 1362 1363 /** 1364 * Build the cached bitmap filename for a shortcut icon. 1365 * 1366 * The filename will be based on the ID, except certain characters will be escaped. 1367 */ openIconFileForWrite(@serIdInt int userId, ShortcutInfo shortcut)1368 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut) 1369 throws IOException { 1370 final File packagePath = new File(getUserBitmapFilePath(userId), 1371 shortcut.getPackage()); 1372 if (!packagePath.isDirectory()) { 1373 packagePath.mkdirs(); 1374 if (!packagePath.isDirectory()) { 1375 throw new IOException("Unable to create directory " + packagePath); 1376 } 1377 SELinux.restorecon(packagePath); 1378 } 1379 1380 final String baseName = String.valueOf(injectCurrentTimeMillis()); 1381 for (int suffix = 0; ; suffix++) { 1382 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png"; 1383 final File file = new File(packagePath, filename); 1384 if (!file.exists()) { 1385 if (DEBUG) { 1386 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath()); 1387 } 1388 return new FileOutputStreamWithPath(file); 1389 } 1390 } 1391 } 1392 saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut)1393 void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) { 1394 if (shortcut.hasIconFile() || shortcut.hasIconResource()) { 1395 return; 1396 } 1397 1398 final long token = injectClearCallingIdentity(); 1399 try { 1400 // Clear icon info on the shortcut. 1401 removeIconLocked(shortcut); 1402 1403 final Icon icon = shortcut.getIcon(); 1404 if (icon == null) { 1405 return; // has no icon 1406 } 1407 int maxIconDimension = mMaxIconDimension; 1408 Bitmap bitmap; 1409 try { 1410 switch (icon.getType()) { 1411 case Icon.TYPE_RESOURCE: { 1412 injectValidateIconResPackage(shortcut, icon); 1413 1414 shortcut.setIconResourceId(icon.getResId()); 1415 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES); 1416 return; 1417 } 1418 case Icon.TYPE_BITMAP: 1419 bitmap = icon.getBitmap(); // Don't recycle in this case. 1420 break; 1421 case Icon.TYPE_ADAPTIVE_BITMAP: { 1422 bitmap = icon.getBitmap(); // Don't recycle in this case. 1423 maxIconDimension *= (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); 1424 break; 1425 } 1426 default: 1427 // This shouldn't happen because we've already validated the icon, but 1428 // just in case. 1429 throw ShortcutInfo.getInvalidIconException(); 1430 } 1431 mShortcutBitmapSaver.saveBitmapLocked(shortcut, 1432 maxIconDimension, mIconPersistFormat, mIconPersistQuality); 1433 } finally { 1434 // Once saved, we won't use the original icon information, so null it out. 1435 shortcut.clearIcon(); 1436 } 1437 } finally { 1438 injectRestoreCallingIdentity(token); 1439 } 1440 } 1441 1442 // Unfortunately we can't do this check in unit tests because we fake creator package names, 1443 // so override in unit tests. 1444 // TODO CTS this case. injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon)1445 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) { 1446 if (!shortcut.getPackage().equals(icon.getResPackage())) { 1447 throw new IllegalArgumentException( 1448 "Icon resource must reside in shortcut owner package"); 1449 } 1450 } 1451 shrinkBitmap(Bitmap in, int maxSize)1452 static Bitmap shrinkBitmap(Bitmap in, int maxSize) { 1453 // Original width/height. 1454 final int ow = in.getWidth(); 1455 final int oh = in.getHeight(); 1456 if ((ow <= maxSize) && (oh <= maxSize)) { 1457 if (DEBUG) { 1458 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh)); 1459 } 1460 return in; 1461 } 1462 final int longerDimension = Math.max(ow, oh); 1463 1464 // New width and height. 1465 final int nw = ow * maxSize / longerDimension; 1466 final int nh = oh * maxSize / longerDimension; 1467 if (DEBUG) { 1468 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d", 1469 ow, oh, nw, nh)); 1470 } 1471 1472 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888); 1473 final Canvas c = new Canvas(scaledBitmap); 1474 1475 final RectF dst = new RectF(0, 0, nw, nh); 1476 1477 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null); 1478 1479 return scaledBitmap; 1480 } 1481 1482 /** 1483 * For a shortcut, update all resource names from resource IDs, and also update all 1484 * resource-based strings. 1485 */ fixUpShortcutResourceNamesAndValues(ShortcutInfo si)1486 void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) { 1487 final Resources publisherRes = injectGetResourcesForApplicationAsUser( 1488 si.getPackage(), si.getUserId()); 1489 if (publisherRes != null) { 1490 final long start = getStatStartTime(); 1491 try { 1492 si.lookupAndFillInResourceNames(publisherRes); 1493 } finally { 1494 logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start); 1495 } 1496 si.resolveResourceStrings(publisherRes); 1497 } 1498 } 1499 1500 // === Caller validation === 1501 isCallerSystem()1502 private boolean isCallerSystem() { 1503 final int callingUid = injectBinderCallingUid(); 1504 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID); 1505 } 1506 isCallerShell()1507 private boolean isCallerShell() { 1508 final int callingUid = injectBinderCallingUid(); 1509 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 1510 } 1511 enforceSystemOrShell()1512 private void enforceSystemOrShell() { 1513 if (!(isCallerSystem() || isCallerShell())) { 1514 throw new SecurityException("Caller must be system or shell"); 1515 } 1516 } 1517 enforceShell()1518 private void enforceShell() { 1519 if (!isCallerShell()) { 1520 throw new SecurityException("Caller must be shell"); 1521 } 1522 } 1523 enforceSystem()1524 private void enforceSystem() { 1525 if (!isCallerSystem()) { 1526 throw new SecurityException("Caller must be system"); 1527 } 1528 } 1529 enforceResetThrottlingPermission()1530 private void enforceResetThrottlingPermission() { 1531 if (isCallerSystem()) { 1532 return; 1533 } 1534 enforceCallingOrSelfPermission( 1535 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null); 1536 } 1537 enforceCallingOrSelfPermission( @onNull String permission, @Nullable String message)1538 private void enforceCallingOrSelfPermission( 1539 @NonNull String permission, @Nullable String message) { 1540 if (isCallerSystem()) { 1541 return; 1542 } 1543 injectEnforceCallingPermission(permission, message); 1544 } 1545 1546 /** 1547 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse 1548 * mockito. So instead we extracted it here and override it in the tests. 1549 */ 1550 @VisibleForTesting injectEnforceCallingPermission( @onNull String permission, @Nullable String message)1551 void injectEnforceCallingPermission( 1552 @NonNull String permission, @Nullable String message) { 1553 mContext.enforceCallingPermission(permission, message); 1554 } 1555 verifyCaller(@onNull String packageName, @UserIdInt int userId)1556 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { 1557 Preconditions.checkStringNotEmpty(packageName, "packageName"); 1558 1559 if (isCallerSystem()) { 1560 return; // no check 1561 } 1562 1563 final int callingUid = injectBinderCallingUid(); 1564 1565 // Otherwise, make sure the arguments are valid. 1566 if (UserHandle.getUserId(callingUid) != userId) { 1567 throw new SecurityException("Invalid user-ID"); 1568 } 1569 if (injectGetPackageUid(packageName, userId) != callingUid) { 1570 throw new SecurityException("Calling package name mismatch"); 1571 } 1572 Preconditions.checkState(!isEphemeralApp(packageName, userId), 1573 "Ephemeral apps can't use ShortcutManager"); 1574 } 1575 1576 // Overridden in unit tests to execute r synchronously. injectPostToHandler(Runnable r)1577 void injectPostToHandler(Runnable r) { 1578 mHandler.post(r); 1579 } 1580 injectRunOnNewThread(Runnable r)1581 void injectRunOnNewThread(Runnable r) { 1582 new Thread(r).start(); 1583 } 1584 1585 /** 1586 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than 1587 * {@link #getMaxActivityShortcuts()}. 1588 */ enforceMaxActivityShortcuts(int numShortcuts)1589 void enforceMaxActivityShortcuts(int numShortcuts) { 1590 if (numShortcuts > mMaxShortcuts) { 1591 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded"); 1592 } 1593 } 1594 1595 /** 1596 * Return the max number of dynamic + manifest shortcuts for each launcher icon. 1597 */ getMaxActivityShortcuts()1598 int getMaxActivityShortcuts() { 1599 return mMaxShortcuts; 1600 } 1601 1602 /** 1603 * - Sends a notification to LauncherApps 1604 * - Write to file 1605 */ packageShortcutsChanged(@onNull String packageName, @UserIdInt int userId)1606 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) { 1607 notifyListeners(packageName, userId); 1608 scheduleSaveUser(userId); 1609 } 1610 notifyListeners(@onNull String packageName, @UserIdInt int userId)1611 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) { 1612 if (DEBUG) { 1613 Slog.d(TAG, String.format( 1614 "Shortcut changes: package=%s, user=%d", packageName, userId)); 1615 } 1616 injectPostToHandler(() -> { 1617 try { 1618 final ArrayList<ShortcutChangeListener> copy; 1619 synchronized (mLock) { 1620 if (!isUserUnlockedL(userId)) { 1621 return; 1622 } 1623 1624 copy = new ArrayList<>(mListeners); 1625 } 1626 // Note onShortcutChanged() needs to be called with the system service permissions. 1627 for (int i = copy.size() - 1; i >= 0; i--) { 1628 copy.get(i).onShortcutChanged(packageName, userId); 1629 } 1630 } catch (Exception ignore) { 1631 } 1632 }); 1633 } 1634 1635 /** 1636 * Clean up / validate an incoming shortcut. 1637 * - Make sure all mandatory fields are set. 1638 * - Make sure the intent's extras are persistable, and them to set 1639 * {@link ShortcutInfo#mIntentPersistableExtrases}. Also clear its extras. 1640 * - Clear flags. 1641 */ fixUpIncomingShortcutInfo(@onNull ShortcutInfo shortcut, boolean forUpdate, boolean forPinRequest)1642 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate, 1643 boolean forPinRequest) { 1644 if (shortcut.isReturnedByServer()) { 1645 Log.w(TAG, 1646 "Re-publishing ShortcutInfo returned by server is not supported." 1647 + " Some information such as icon may lost from shortcut."); 1648 } 1649 Preconditions.checkNotNull(shortcut, "Null shortcut detected"); 1650 if (shortcut.getActivity() != null) { 1651 Preconditions.checkState( 1652 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()), 1653 "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not" 1654 + " belong to package " + shortcut.getPackage()); 1655 Preconditions.checkState( 1656 injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()), 1657 "Cannot publish shortcut: activity " + shortcut.getActivity() + " is not" 1658 + " main activity"); 1659 } 1660 1661 if (!forUpdate) { 1662 shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest); 1663 if (!forPinRequest) { 1664 Preconditions.checkState(shortcut.getActivity() != null, 1665 "Cannot publish shortcut: target activity is not set"); 1666 } 1667 } 1668 if (shortcut.getIcon() != null) { 1669 ShortcutInfo.validateIcon(shortcut.getIcon()); 1670 } 1671 1672 shortcut.replaceFlags(0); 1673 } 1674 fixUpIncomingShortcutInfo(@onNull ShortcutInfo shortcut, boolean forUpdate)1675 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { 1676 fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false); 1677 } 1678 validateShortcutForPinRequest(@onNull ShortcutInfo shortcut)1679 public void validateShortcutForPinRequest(@NonNull ShortcutInfo shortcut) { 1680 fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false, /*forPinRequest=*/ true); 1681 } 1682 1683 /** 1684 * When a shortcut has no target activity, set the default one from the package. 1685 */ fillInDefaultActivity(List<ShortcutInfo> shortcuts)1686 private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) { 1687 ComponentName defaultActivity = null; 1688 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1689 final ShortcutInfo si = shortcuts.get(i); 1690 if (si.getActivity() == null) { 1691 if (defaultActivity == null) { 1692 defaultActivity = injectGetDefaultMainActivity( 1693 si.getPackage(), si.getUserId()); 1694 Preconditions.checkState(defaultActivity != null, 1695 "Launcher activity not found for package " + si.getPackage()); 1696 } 1697 si.setActivity(defaultActivity); 1698 } 1699 } 1700 } 1701 assignImplicitRanks(List<ShortcutInfo> shortcuts)1702 private void assignImplicitRanks(List<ShortcutInfo> shortcuts) { 1703 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1704 shortcuts.get(i).setImplicitRank(i); 1705 } 1706 } 1707 setReturnedByServer(List<ShortcutInfo> shortcuts)1708 private List<ShortcutInfo> setReturnedByServer(List<ShortcutInfo> shortcuts) { 1709 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1710 shortcuts.get(i).setReturnedByServer(); 1711 } 1712 return shortcuts; 1713 } 1714 1715 // === APIs === 1716 1717 @Override setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId)1718 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1719 @UserIdInt int userId) { 1720 verifyCaller(packageName, userId); 1721 1722 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1723 final int size = newShortcuts.size(); 1724 1725 final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( 1726 injectBinderCallingPid(), injectBinderCallingUid()); 1727 1728 synchronized (mLock) { 1729 throwIfUserLockedL(userId); 1730 1731 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 1732 1733 ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); 1734 1735 fillInDefaultActivity(newShortcuts); 1736 1737 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); 1738 1739 // Throttling. 1740 if (!ps.tryApiCall(unlimited)) { 1741 return false; 1742 } 1743 1744 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1745 ps.clearAllImplicitRanks(); 1746 assignImplicitRanks(newShortcuts); 1747 1748 for (int i = 0; i < size; i++) { 1749 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); 1750 } 1751 1752 // First, remove all un-pinned; dynamic shortcuts 1753 ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); 1754 1755 // Then, add/update all. We need to make sure to take over "pinned" flag. 1756 for (int i = 0; i < size; i++) { 1757 final ShortcutInfo newShortcut = newShortcuts.get(i); 1758 ps.addOrReplaceDynamicShortcut(newShortcut); 1759 } 1760 1761 // Lastly, adjust the ranks. 1762 ps.adjustRanks(); 1763 } 1764 packageShortcutsChanged(packageName, userId); 1765 1766 verifyStates(); 1767 1768 return true; 1769 } 1770 1771 @Override updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId)1772 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1773 @UserIdInt int userId) { 1774 verifyCaller(packageName, userId); 1775 1776 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1777 final int size = newShortcuts.size(); 1778 1779 final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( 1780 injectBinderCallingPid(), injectBinderCallingUid()); 1781 1782 synchronized (mLock) { 1783 throwIfUserLockedL(userId); 1784 1785 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 1786 1787 ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); 1788 1789 // For update, don't fill in the default activity. Having null activity means 1790 // "don't update the activity" here. 1791 1792 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); 1793 1794 // Throttling. 1795 if (!ps.tryApiCall(unlimited)) { 1796 return false; 1797 } 1798 1799 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1800 ps.clearAllImplicitRanks(); 1801 assignImplicitRanks(newShortcuts); 1802 1803 for (int i = 0; i < size; i++) { 1804 final ShortcutInfo source = newShortcuts.get(i); 1805 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); 1806 1807 final ShortcutInfo target = ps.findShortcutById(source.getId()); 1808 1809 // Invisible shortcuts can't be updated. 1810 if (target == null || !target.isVisibleToPublisher()) { 1811 continue; 1812 } 1813 1814 if (target.isEnabled() != source.isEnabled()) { 1815 Slog.w(TAG, 1816 "ShortcutInfo.enabled cannot be changed with updateShortcuts()"); 1817 } 1818 1819 // When updating the rank, we need to insert between existing ranks, so set 1820 // this setRankChanged, and also copy the implicit rank fo adjustRanks(). 1821 if (source.hasRank()) { 1822 target.setRankChanged(); 1823 target.setImplicitRank(source.getImplicitRank()); 1824 } 1825 1826 final boolean replacingIcon = (source.getIcon() != null); 1827 if (replacingIcon) { 1828 removeIconLocked(target); 1829 } 1830 1831 // Note copyNonNullFieldsFrom() does the "updatable with?" check too. 1832 target.copyNonNullFieldsFrom(source); 1833 target.setTimestamp(injectCurrentTimeMillis()); 1834 1835 if (replacingIcon) { 1836 saveIconAndFixUpShortcutLocked(target); 1837 } 1838 1839 // When we're updating any resource related fields, re-extract the res names and 1840 // the values. 1841 if (replacingIcon || source.hasStringResources()) { 1842 fixUpShortcutResourceNamesAndValues(target); 1843 } 1844 } 1845 1846 // Lastly, adjust the ranks. 1847 ps.adjustRanks(); 1848 } 1849 packageShortcutsChanged(packageName, userId); 1850 1851 verifyStates(); 1852 1853 return true; 1854 } 1855 1856 @Override addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, @UserIdInt int userId)1857 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1858 @UserIdInt int userId) { 1859 verifyCaller(packageName, userId); 1860 1861 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1862 final int size = newShortcuts.size(); 1863 1864 final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( 1865 injectBinderCallingPid(), injectBinderCallingUid()); 1866 1867 synchronized (mLock) { 1868 throwIfUserLockedL(userId); 1869 1870 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 1871 1872 ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true); 1873 1874 fillInDefaultActivity(newShortcuts); 1875 1876 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); 1877 1878 // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). 1879 ps.clearAllImplicitRanks(); 1880 assignImplicitRanks(newShortcuts); 1881 1882 // Throttling. 1883 if (!ps.tryApiCall(unlimited)) { 1884 return false; 1885 } 1886 for (int i = 0; i < size; i++) { 1887 final ShortcutInfo newShortcut = newShortcuts.get(i); 1888 1889 // Validate the shortcut. 1890 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); 1891 1892 // When ranks are changing, we need to insert between ranks, so set the 1893 // "rank changed" flag. 1894 newShortcut.setRankChanged(); 1895 1896 // Add it. 1897 ps.addOrReplaceDynamicShortcut(newShortcut); 1898 } 1899 1900 // Lastly, adjust the ranks. 1901 ps.adjustRanks(); 1902 } 1903 packageShortcutsChanged(packageName, userId); 1904 1905 verifyStates(); 1906 1907 return true; 1908 } 1909 1910 @Override requestPinShortcut(String packageName, ShortcutInfo shortcut, IntentSender resultIntent, int userId)1911 public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, 1912 IntentSender resultIntent, int userId) { 1913 Preconditions.checkNotNull(shortcut); 1914 Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); 1915 return requestPinItem(packageName, userId, shortcut, null, null, resultIntent); 1916 } 1917 1918 @Override createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)1919 public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId) 1920 throws RemoteException { 1921 Preconditions.checkNotNull(shortcut); 1922 Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); 1923 verifyCaller(packageName, userId); 1924 1925 final Intent ret; 1926 synchronized (mLock) { 1927 throwIfUserLockedL(userId); 1928 1929 // Send request to the launcher, if supported. 1930 ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId); 1931 } 1932 1933 verifyStates(); 1934 return ret; 1935 } 1936 1937 /** 1938 * Handles {@link #requestPinShortcut} and {@link ShortcutServiceInternal#requestPinAppWidget}. 1939 * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}. 1940 * Either {@param shortcut} or {@param appWidget} should be non-null. 1941 */ requestPinItem(String packageName, int userId, ShortcutInfo shortcut, AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent)1942 private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut, 1943 AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) { 1944 verifyCaller(packageName, userId); 1945 1946 final boolean ret; 1947 synchronized (mLock) { 1948 throwIfUserLockedL(userId); 1949 1950 Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()), 1951 "Calling application must have a foreground activity or a foreground service"); 1952 1953 // If it's a pin shortcut request, and there's already a shortcut with the same ID 1954 // that's not visible to the caller (i.e. restore-blocked; meaning it's pinned by 1955 // someone already), then we just replace the existing one with this new one, 1956 // and then proceed the rest of the process. 1957 if (shortcut != null) { 1958 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked( 1959 packageName, userId); 1960 final String id = shortcut.getId(); 1961 if (ps.isShortcutExistsAndInvisibleToPublisher(id)) { 1962 1963 ps.updateInvisibleShortcutForPinRequestWith(shortcut); 1964 1965 packageShortcutsChanged(packageName, userId); 1966 } 1967 } 1968 1969 // Send request to the launcher, if supported. 1970 ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, extras, 1971 userId, resultIntent); 1972 } 1973 1974 verifyStates(); 1975 1976 return ret; 1977 } 1978 1979 @Override disableShortcuts(String packageName, List shortcutIds, CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId)1980 public void disableShortcuts(String packageName, List shortcutIds, 1981 CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) { 1982 verifyCaller(packageName, userId); 1983 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1984 1985 synchronized (mLock) { 1986 throwIfUserLockedL(userId); 1987 1988 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 1989 1990 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds, 1991 /*ignoreInvisible=*/ true); 1992 1993 final String disabledMessageString = 1994 (disabledMessage == null) ? null : disabledMessage.toString(); 1995 1996 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1997 final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); 1998 if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { 1999 continue; 2000 } 2001 ps.disableWithId(id, 2002 disabledMessageString, disabledMessageResId, 2003 /* overrideImmutable=*/ false, /*ignoreInvisible=*/ true, 2004 ShortcutInfo.DISABLED_REASON_BY_APP); 2005 } 2006 2007 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. 2008 ps.adjustRanks(); 2009 } 2010 packageShortcutsChanged(packageName, userId); 2011 2012 verifyStates(); 2013 } 2014 2015 @Override enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId)2016 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { 2017 verifyCaller(packageName, userId); 2018 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 2019 2020 synchronized (mLock) { 2021 throwIfUserLockedL(userId); 2022 2023 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2024 2025 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds, 2026 /*ignoreInvisible=*/ true); 2027 2028 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 2029 final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); 2030 if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { 2031 continue; 2032 } 2033 ps.enableWithId(id); 2034 } 2035 } 2036 packageShortcutsChanged(packageName, userId); 2037 2038 verifyStates(); 2039 } 2040 2041 @Override removeDynamicShortcuts(String packageName, List shortcutIds, @UserIdInt int userId)2042 public void removeDynamicShortcuts(String packageName, List shortcutIds, 2043 @UserIdInt int userId) { 2044 verifyCaller(packageName, userId); 2045 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 2046 2047 synchronized (mLock) { 2048 throwIfUserLockedL(userId); 2049 2050 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2051 2052 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds, 2053 /*ignoreInvisible=*/ true); 2054 2055 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 2056 final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)); 2057 if (!ps.isShortcutExistsAndVisibleToPublisher(id)) { 2058 continue; 2059 } 2060 ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true); 2061 } 2062 2063 // We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks. 2064 ps.adjustRanks(); 2065 } 2066 packageShortcutsChanged(packageName, userId); 2067 2068 verifyStates(); 2069 } 2070 2071 @Override removeAllDynamicShortcuts(String packageName, @UserIdInt int userId)2072 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { 2073 verifyCaller(packageName, userId); 2074 2075 synchronized (mLock) { 2076 throwIfUserLockedL(userId); 2077 2078 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2079 ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true); 2080 } 2081 packageShortcutsChanged(packageName, userId); 2082 2083 verifyStates(); 2084 } 2085 2086 @Override getDynamicShortcuts(String packageName, @UserIdInt int userId)2087 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, 2088 @UserIdInt int userId) { 2089 verifyCaller(packageName, userId); 2090 2091 synchronized (mLock) { 2092 throwIfUserLockedL(userId); 2093 2094 return getShortcutsWithQueryLocked( 2095 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 2096 ShortcutInfo::isDynamicVisible); 2097 } 2098 } 2099 2100 @Override getManifestShortcuts(String packageName, @UserIdInt int userId)2101 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, 2102 @UserIdInt int userId) { 2103 verifyCaller(packageName, userId); 2104 2105 synchronized (mLock) { 2106 throwIfUserLockedL(userId); 2107 2108 return getShortcutsWithQueryLocked( 2109 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 2110 ShortcutInfo::isManifestVisible); 2111 } 2112 } 2113 2114 @Override getPinnedShortcuts(String packageName, @UserIdInt int userId)2115 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, 2116 @UserIdInt int userId) { 2117 verifyCaller(packageName, userId); 2118 2119 synchronized (mLock) { 2120 throwIfUserLockedL(userId); 2121 2122 return getShortcutsWithQueryLocked( 2123 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 2124 ShortcutInfo::isPinnedVisible); 2125 } 2126 } 2127 2128 @GuardedBy("mLock") getShortcutsWithQueryLocked(@onNull String packageName, @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query)2129 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, 2130 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { 2131 2132 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 2133 2134 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2135 ps.findAll(ret, query, cloneFlags); 2136 2137 return new ParceledListSlice<>(setReturnedByServer(ret)); 2138 } 2139 2140 @Override getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId)2141 public int getMaxShortcutCountPerActivity(String packageName, @UserIdInt int userId) 2142 throws RemoteException { 2143 verifyCaller(packageName, userId); 2144 2145 return mMaxShortcuts; 2146 } 2147 2148 @Override getRemainingCallCount(String packageName, @UserIdInt int userId)2149 public int getRemainingCallCount(String packageName, @UserIdInt int userId) { 2150 verifyCaller(packageName, userId); 2151 2152 final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( 2153 injectBinderCallingPid(), injectBinderCallingUid()); 2154 2155 synchronized (mLock) { 2156 throwIfUserLockedL(userId); 2157 2158 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2159 return mMaxUpdatesPerInterval - ps.getApiCallCount(unlimited); 2160 } 2161 } 2162 2163 @Override getRateLimitResetTime(String packageName, @UserIdInt int userId)2164 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { 2165 verifyCaller(packageName, userId); 2166 2167 synchronized (mLock) { 2168 throwIfUserLockedL(userId); 2169 2170 return getNextResetTimeLocked(); 2171 } 2172 } 2173 2174 @Override getIconMaxDimensions(String packageName, int userId)2175 public int getIconMaxDimensions(String packageName, int userId) { 2176 verifyCaller(packageName, userId); 2177 2178 synchronized (mLock) { 2179 return mMaxIconDimension; 2180 } 2181 } 2182 2183 @Override reportShortcutUsed(String packageName, String shortcutId, int userId)2184 public void reportShortcutUsed(String packageName, String shortcutId, int userId) { 2185 verifyCaller(packageName, userId); 2186 2187 Preconditions.checkNotNull(shortcutId); 2188 2189 if (DEBUG) { 2190 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", 2191 shortcutId, packageName, userId)); 2192 } 2193 2194 synchronized (mLock) { 2195 throwIfUserLockedL(userId); 2196 2197 final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); 2198 2199 if (ps.findShortcutById(shortcutId) == null) { 2200 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", 2201 packageName, shortcutId)); 2202 return; 2203 } 2204 } 2205 2206 final long token = injectClearCallingIdentity(); 2207 try { 2208 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); 2209 } finally { 2210 injectRestoreCallingIdentity(token); 2211 } 2212 } 2213 2214 @Override isRequestPinItemSupported(int callingUserId, int requestType)2215 public boolean isRequestPinItemSupported(int callingUserId, int requestType) { 2216 final long token = injectClearCallingIdentity(); 2217 try { 2218 return mShortcutRequestPinProcessor 2219 .isRequestPinItemSupported(callingUserId, requestType); 2220 } finally { 2221 injectRestoreCallingIdentity(token); 2222 } 2223 } 2224 2225 /** 2226 * Reset all throttling, for developer options and command line. Only system/shell can call 2227 * it. 2228 */ 2229 @Override resetThrottling()2230 public void resetThrottling() { 2231 enforceSystemOrShell(); 2232 2233 resetThrottlingInner(getCallingUserId()); 2234 } 2235 resetThrottlingInner(@serIdInt int userId)2236 void resetThrottlingInner(@UserIdInt int userId) { 2237 synchronized (mLock) { 2238 if (!isUserUnlockedL(userId)) { 2239 Log.w(TAG, "User " + userId + " is locked or not running"); 2240 return; 2241 } 2242 2243 getUserShortcutsLocked(userId).resetThrottling(); 2244 } 2245 scheduleSaveUser(userId); 2246 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId); 2247 } 2248 resetAllThrottlingInner()2249 void resetAllThrottlingInner() { 2250 synchronized (mLock) { 2251 mRawLastResetTime = injectCurrentTimeMillis(); 2252 } 2253 scheduleSaveBaseState(); 2254 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users"); 2255 } 2256 2257 @Override onApplicationActive(String packageName, int userId)2258 public void onApplicationActive(String packageName, int userId) { 2259 if (DEBUG) { 2260 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); 2261 } 2262 enforceResetThrottlingPermission(); 2263 2264 synchronized (mLock) { 2265 if (!isUserUnlockedL(userId)) { 2266 // This is called by system UI, so no need to throw. Just ignore. 2267 return; 2268 } 2269 2270 getPackageShortcutsLocked(packageName, userId) 2271 .resetRateLimitingForCommandLineNoSaving(); 2272 saveUserLocked(userId); 2273 } 2274 } 2275 2276 // We override this method in unit tests to do a simpler check. hasShortcutHostPermission(@onNull String callingPackage, int userId, int callingPid, int callingUid)2277 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId, 2278 int callingPid, int callingUid) { 2279 if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) { 2280 return true; 2281 } 2282 final long start = getStatStartTime(); 2283 try { 2284 return hasShortcutHostPermissionInner(callingPackage, userId); 2285 } finally { 2286 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); 2287 } 2288 } 2289 canSeeAnyPinnedShortcut(@onNull String callingPackage, int userId, int callingPid, int callingUid)2290 boolean canSeeAnyPinnedShortcut(@NonNull String callingPackage, int userId, 2291 int callingPid, int callingUid) { 2292 if (injectHasAccessShortcutsPermission(callingPid, callingUid)) { 2293 return true; 2294 } 2295 synchronized (mLock) { 2296 return getNonPersistentUserLocked(userId).hasHostPackage(callingPackage); 2297 } 2298 } 2299 2300 /** 2301 * Returns true if the caller has the "ACCESS_SHORTCUTS" permission. 2302 */ 2303 @VisibleForTesting injectHasAccessShortcutsPermission(int callingPid, int callingUid)2304 boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) { 2305 return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS, 2306 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; 2307 } 2308 2309 /** 2310 * Returns true if the caller has the "UNLIMITED_SHORTCUTS_API_CALLS" permission. 2311 */ 2312 @VisibleForTesting injectHasUnlimitedShortcutsApiCallsPermission(int callingPid, int callingUid)2313 boolean injectHasUnlimitedShortcutsApiCallsPermission(int callingPid, int callingUid) { 2314 return mContext.checkPermission(permission.UNLIMITED_SHORTCUTS_API_CALLS, 2315 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; 2316 } 2317 2318 // This method is extracted so we can directly call this method from unit tests, 2319 // even when hasShortcutPermission() is overridden. 2320 @VisibleForTesting hasShortcutHostPermissionInner(@onNull String packageName, int userId)2321 boolean hasShortcutHostPermissionInner(@NonNull String packageName, int userId) { 2322 synchronized (mLock) { 2323 throwIfUserLockedL(userId); 2324 2325 final ShortcutUser user = getUserShortcutsLocked(userId); 2326 2327 // Always trust the cached component. 2328 final ComponentName cached = user.getCachedLauncher(); 2329 if (cached != null) { 2330 if (cached.getPackageName().equals(packageName)) { 2331 return true; 2332 } 2333 } 2334 // If the cached one doesn't match, then go ahead 2335 2336 final ComponentName detected = getDefaultLauncher(userId); 2337 2338 // Update the cache. 2339 user.setLauncher(detected); 2340 if (detected != null) { 2341 if (DEBUG) { 2342 Slog.v(TAG, "Detected launcher: " + detected); 2343 } 2344 return detected.getPackageName().equals(packageName); 2345 } else { 2346 // Default launcher not found. 2347 return false; 2348 } 2349 } 2350 } 2351 2352 @Nullable getDefaultLauncher(@serIdInt int userId)2353 ComponentName getDefaultLauncher(@UserIdInt int userId) { 2354 final long start = getStatStartTime(); 2355 final long token = injectClearCallingIdentity(); 2356 try { 2357 synchronized (mLock) { 2358 throwIfUserLockedL(userId); 2359 2360 final ShortcutUser user = getUserShortcutsLocked(userId); 2361 2362 final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); 2363 2364 // Default launcher from package manager. 2365 final long startGetHomeActivitiesAsUser = getStatStartTime(); 2366 final ComponentName defaultLauncher = mPackageManagerInternal 2367 .getHomeActivitiesAsUser(allHomeCandidates, userId); 2368 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); 2369 2370 ComponentName detected = null; 2371 if (defaultLauncher != null) { 2372 detected = defaultLauncher; 2373 if (DEBUG) { 2374 Slog.v(TAG, "Default launcher from PM: " + detected); 2375 } 2376 } else { 2377 detected = user.getLastKnownLauncher(); 2378 2379 if (detected != null) { 2380 if (injectIsActivityEnabledAndExported(detected, userId)) { 2381 if (DEBUG) { 2382 Slog.v(TAG, "Cached launcher: " + detected); 2383 } 2384 } else { 2385 Slog.w(TAG, "Cached launcher " + detected + " no longer exists"); 2386 detected = null; 2387 user.clearLauncher(); 2388 } 2389 } 2390 } 2391 2392 if (detected == null) { 2393 // If we reach here, that means it's the first check since the user was created, 2394 // and there's already multiple launchers and there's no default set. 2395 // Find the system one with the highest priority. 2396 // (We need to check the priority too because of FallbackHome in Settings.) 2397 // If there's no system launcher yet, then no one can access shortcuts, until 2398 // the user explicitly 2399 final int size = allHomeCandidates.size(); 2400 2401 int lastPriority = Integer.MIN_VALUE; 2402 for (int i = 0; i < size; i++) { 2403 final ResolveInfo ri = allHomeCandidates.get(i); 2404 if (!ri.activityInfo.applicationInfo.isSystemApp()) { 2405 continue; 2406 } 2407 if (DEBUG) { 2408 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d", 2409 ri.activityInfo.getComponentName(), ri.priority)); 2410 } 2411 if (ri.priority < lastPriority) { 2412 continue; 2413 } 2414 detected = ri.activityInfo.getComponentName(); 2415 lastPriority = ri.priority; 2416 } 2417 } 2418 return detected; 2419 } 2420 } finally { 2421 injectRestoreCallingIdentity(token); 2422 logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start); 2423 } 2424 } 2425 setShortcutHostPackage(@onNull String type, @Nullable String packageName, int userId)2426 public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName, 2427 int userId) { 2428 synchronized (mLock) { 2429 getNonPersistentUserLocked(userId).setShortcutHostPackage(type, packageName); 2430 } 2431 } 2432 2433 // === House keeping === 2434 cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId, boolean appStillExists)2435 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId, 2436 boolean appStillExists) { 2437 synchronized (mLock) { 2438 forEachLoadedUserLocked(user -> 2439 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId, 2440 appStillExists)); 2441 } 2442 } 2443 2444 /** 2445 * Remove all the information associated with a package. This will really remove all the 2446 * information, including the restore information (i.e. it'll remove packages even if they're 2447 * shadow). 2448 * 2449 * This is called when an app is uninstalled, or an app gets "clear data"ed. 2450 */ 2451 @GuardedBy("mLock") 2452 @VisibleForTesting cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId, boolean appStillExists)2453 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId, 2454 boolean appStillExists) { 2455 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId); 2456 2457 final ShortcutUser user = getUserShortcutsLocked(owningUserId); 2458 boolean doNotify = false; 2459 2460 // First, remove the package from the package list (if the package is a publisher). 2461 if (packageUserId == owningUserId) { 2462 if (user.removePackage(packageName) != null) { 2463 doNotify = true; 2464 } 2465 } 2466 2467 // Also remove from the launcher list (if the package is a launcher). 2468 user.removeLauncher(packageUserId, packageName); 2469 2470 // Then remove pinned shortcuts from all launchers. 2471 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId)); 2472 2473 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous 2474 // step. Remove them too. 2475 user.forAllPackages(p -> p.refreshPinnedFlags()); 2476 2477 scheduleSaveUser(owningUserId); 2478 2479 if (doNotify) { 2480 notifyListeners(packageName, owningUserId); 2481 } 2482 2483 // If the app still exists (i.e. data cleared), we need to re-publish manifest shortcuts. 2484 if (appStillExists && (packageUserId == owningUserId)) { 2485 // This will do the notification and save when needed, so do it after the above 2486 // notifyListeners. 2487 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true); 2488 } 2489 2490 if (!wasUserLoaded) { 2491 // Note this will execute the scheduled save. 2492 unloadUserLocked(owningUserId); 2493 } 2494 } 2495 2496 /** 2497 * Entry point from {@link LauncherApps}. 2498 */ 2499 private class LocalService extends ShortcutServiceInternal { 2500 2501 @Override getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, @Nullable ComponentName componentName, int queryFlags, int userId, int callingPid, int callingUid)2502 public List<ShortcutInfo> getShortcuts(int launcherUserId, 2503 @NonNull String callingPackage, long changedSince, 2504 @Nullable String packageName, @Nullable List<String> shortcutIds, 2505 @Nullable ComponentName componentName, 2506 int queryFlags, int userId, int callingPid, int callingUid) { 2507 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 2508 2509 final boolean cloneKeyFieldOnly = 2510 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); 2511 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO 2512 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; 2513 if (packageName == null) { 2514 shortcutIds = null; // LauncherAppsService already threw for it though. 2515 } 2516 2517 synchronized (mLock) { 2518 throwIfUserLockedL(userId); 2519 throwIfUserLockedL(launcherUserId); 2520 2521 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2522 .attemptToRestoreIfNeededAndSave(); 2523 2524 if (packageName != null) { 2525 getShortcutsInnerLocked(launcherUserId, 2526 callingPackage, packageName, shortcutIds, changedSince, 2527 componentName, queryFlags, userId, ret, cloneFlag, 2528 callingPid, callingUid); 2529 } else { 2530 final List<String> shortcutIdsF = shortcutIds; 2531 getUserShortcutsLocked(userId).forAllPackages(p -> { 2532 getShortcutsInnerLocked(launcherUserId, 2533 callingPackage, p.getPackageName(), shortcutIdsF, changedSince, 2534 componentName, queryFlags, userId, ret, cloneFlag, 2535 callingPid, callingUid); 2536 }); 2537 } 2538 } 2539 return setReturnedByServer(ret); 2540 } 2541 2542 @GuardedBy("ShortcutService.this.mLock") getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, @Nullable ComponentName componentName, int queryFlags, int userId, ArrayList<ShortcutInfo> ret, int cloneFlag, int callingPid, int callingUid)2543 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, 2544 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, 2545 @Nullable ComponentName componentName, int queryFlags, 2546 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag, 2547 int callingPid, int callingUid) { 2548 final ArraySet<String> ids = shortcutIds == null ? null 2549 : new ArraySet<>(shortcutIds); 2550 2551 final ShortcutUser user = getUserShortcutsLocked(userId); 2552 final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName); 2553 if (p == null) { 2554 return; // No need to instantiate ShortcutPackage. 2555 } 2556 final boolean matchDynamic = (queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0; 2557 final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0; 2558 final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0; 2559 2560 final boolean canAccessAllShortcuts = 2561 canSeeAnyPinnedShortcut(callingPackage, launcherUserId, callingPid, callingUid); 2562 2563 final boolean getPinnedByAnyLauncher = 2564 canAccessAllShortcuts && 2565 ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0); 2566 2567 p.findAll(ret, 2568 (ShortcutInfo si) -> { 2569 if (si.getLastChangedTimestamp() < changedSince) { 2570 return false; 2571 } 2572 if (ids != null && !ids.contains(si.getId())) { 2573 return false; 2574 } 2575 if (componentName != null) { 2576 if (si.getActivity() != null 2577 && !si.getActivity().equals(componentName)) { 2578 return false; 2579 } 2580 } 2581 if (matchDynamic && si.isDynamic()) { 2582 return true; 2583 } 2584 if ((matchPinned || getPinnedByAnyLauncher) && si.isPinned()) { 2585 return true; 2586 } 2587 if (matchManifest && si.isDeclaredInManifest()) { 2588 return true; 2589 } 2590 return false; 2591 }, cloneFlag, callingPackage, launcherUserId, getPinnedByAnyLauncher); 2592 } 2593 2594 @Override isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId)2595 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, 2596 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2597 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2598 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2599 2600 synchronized (mLock) { 2601 throwIfUserLockedL(userId); 2602 throwIfUserLockedL(launcherUserId); 2603 2604 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2605 .attemptToRestoreIfNeededAndSave(); 2606 2607 final ShortcutInfo si = getShortcutInfoLocked( 2608 launcherUserId, callingPackage, packageName, shortcutId, userId, 2609 /*getPinnedByAnyLauncher=*/ false); 2610 return si != null && si.isPinned(); 2611 } 2612 } 2613 2614 @GuardedBy("ShortcutService.this.mLock") getShortcutInfoLocked( int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId, boolean getPinnedByAnyLauncher)2615 private ShortcutInfo getShortcutInfoLocked( 2616 int launcherUserId, @NonNull String callingPackage, 2617 @NonNull String packageName, @NonNull String shortcutId, int userId, 2618 boolean getPinnedByAnyLauncher) { 2619 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2620 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2621 2622 throwIfUserLockedL(userId); 2623 throwIfUserLockedL(launcherUserId); 2624 2625 final ShortcutPackage p = getUserShortcutsLocked(userId) 2626 .getPackageShortcutsIfExists(packageName); 2627 if (p == null) { 2628 return null; 2629 } 2630 2631 final ArrayList<ShortcutInfo> list = new ArrayList<>(1); 2632 p.findAll(list, 2633 (ShortcutInfo si) -> shortcutId.equals(si.getId()), 2634 /* clone flags=*/ 0, callingPackage, launcherUserId, getPinnedByAnyLauncher); 2635 return list.size() == 0 ? null : list.get(0); 2636 } 2637 2638 @Override pinShortcuts(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId)2639 public void pinShortcuts(int launcherUserId, 2640 @NonNull String callingPackage, @NonNull String packageName, 2641 @NonNull List<String> shortcutIds, int userId) { 2642 // Calling permission must be checked by LauncherAppsImpl. 2643 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2644 Preconditions.checkNotNull(shortcutIds, "shortcutIds"); 2645 2646 synchronized (mLock) { 2647 throwIfUserLockedL(userId); 2648 throwIfUserLockedL(launcherUserId); 2649 2650 final ShortcutLauncher launcher = 2651 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); 2652 launcher.attemptToRestoreIfNeededAndSave(); 2653 2654 launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false); 2655 } 2656 packageShortcutsChanged(packageName, userId); 2657 2658 verifyStates(); 2659 } 2660 2661 @Override createShortcutIntents(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId, int callingPid, int callingUid)2662 public Intent[] createShortcutIntents(int launcherUserId, 2663 @NonNull String callingPackage, 2664 @NonNull String packageName, @NonNull String shortcutId, int userId, 2665 int callingPid, int callingUid) { 2666 // Calling permission must be checked by LauncherAppsImpl. 2667 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); 2668 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); 2669 2670 synchronized (mLock) { 2671 throwIfUserLockedL(userId); 2672 throwIfUserLockedL(launcherUserId); 2673 2674 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2675 .attemptToRestoreIfNeededAndSave(); 2676 2677 final boolean getPinnedByAnyLauncher = 2678 canSeeAnyPinnedShortcut(callingPackage, launcherUserId, 2679 callingPid, callingUid); 2680 2681 // Make sure the shortcut is actually visible to the launcher. 2682 final ShortcutInfo si = getShortcutInfoLocked( 2683 launcherUserId, callingPackage, packageName, shortcutId, userId, 2684 getPinnedByAnyLauncher); 2685 // "si == null" should suffice here, but check the flags too just to make sure. 2686 if (si == null || !si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) { 2687 Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled"); 2688 return null; 2689 } 2690 return si.getIntents(); 2691 } 2692 } 2693 2694 @Override addListener(@onNull ShortcutChangeListener listener)2695 public void addListener(@NonNull ShortcutChangeListener listener) { 2696 synchronized (mLock) { 2697 mListeners.add(Preconditions.checkNotNull(listener)); 2698 } 2699 } 2700 2701 @Override getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId)2702 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, 2703 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2704 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2705 Preconditions.checkNotNull(packageName, "packageName"); 2706 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2707 2708 synchronized (mLock) { 2709 throwIfUserLockedL(userId); 2710 throwIfUserLockedL(launcherUserId); 2711 2712 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2713 .attemptToRestoreIfNeededAndSave(); 2714 2715 final ShortcutPackage p = getUserShortcutsLocked(userId) 2716 .getPackageShortcutsIfExists(packageName); 2717 if (p == null) { 2718 return 0; 2719 } 2720 2721 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2722 return (shortcutInfo != null && shortcutInfo.hasIconResource()) 2723 ? shortcutInfo.getIconResourceId() : 0; 2724 } 2725 } 2726 2727 @Override getShortcutIconFd(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId)2728 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId, 2729 @NonNull String callingPackage, @NonNull String packageName, 2730 @NonNull String shortcutId, int userId) { 2731 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2732 Preconditions.checkNotNull(packageName, "packageName"); 2733 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2734 2735 synchronized (mLock) { 2736 throwIfUserLockedL(userId); 2737 throwIfUserLockedL(launcherUserId); 2738 2739 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2740 .attemptToRestoreIfNeededAndSave(); 2741 2742 final ShortcutPackage p = getUserShortcutsLocked(userId) 2743 .getPackageShortcutsIfExists(packageName); 2744 if (p == null) { 2745 return null; 2746 } 2747 2748 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2749 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) { 2750 return null; 2751 } 2752 final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo); 2753 if (path == null) { 2754 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()"); 2755 return null; 2756 } 2757 try { 2758 return ParcelFileDescriptor.open( 2759 new File(path), 2760 ParcelFileDescriptor.MODE_READ_ONLY); 2761 } catch (FileNotFoundException e) { 2762 Slog.e(TAG, "Icon file not found: " + path); 2763 return null; 2764 } 2765 } 2766 } 2767 2768 @Override hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage, int callingPid, int callingUid)2769 public boolean hasShortcutHostPermission(int launcherUserId, 2770 @NonNull String callingPackage, int callingPid, int callingUid) { 2771 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId, 2772 callingPid, callingUid); 2773 } 2774 2775 @Override setShortcutHostPackage(@onNull String type, @Nullable String packageName, int userId)2776 public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName, 2777 int userId) { 2778 ShortcutService.this.setShortcutHostPackage(type, packageName, userId); 2779 } 2780 2781 @Override requestPinAppWidget(@onNull String callingPackage, @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras, @Nullable IntentSender resultIntent, int userId)2782 public boolean requestPinAppWidget(@NonNull String callingPackage, 2783 @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras, 2784 @Nullable IntentSender resultIntent, int userId) { 2785 Preconditions.checkNotNull(appWidget); 2786 return requestPinItem(callingPackage, userId, null, appWidget, extras, resultIntent); 2787 } 2788 2789 @Override isRequestPinItemSupported(int callingUserId, int requestType)2790 public boolean isRequestPinItemSupported(int callingUserId, int requestType) { 2791 return ShortcutService.this.isRequestPinItemSupported(callingUserId, requestType); 2792 } 2793 2794 @Override isForegroundDefaultLauncher(@onNull String callingPackage, int callingUid)2795 public boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid) { 2796 Preconditions.checkNotNull(callingPackage); 2797 2798 final int userId = UserHandle.getUserId(callingUid); 2799 final ComponentName defaultLauncher = getDefaultLauncher(userId); 2800 if (defaultLauncher == null) { 2801 return false; 2802 } 2803 if (!callingPackage.equals(defaultLauncher.getPackageName())) { 2804 return false; 2805 } 2806 synchronized (mLock) { 2807 if (!isUidForegroundLocked(callingUid)) { 2808 return false; 2809 } 2810 } 2811 return true; 2812 } 2813 } 2814 2815 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 2816 @Override 2817 public void onReceive(Context context, Intent intent) { 2818 if (!mBootCompleted.get()) { 2819 return; // Boot not completed, ignore the broadcast. 2820 } 2821 try { 2822 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { 2823 handleLocaleChanged(); 2824 } 2825 } catch (Exception e) { 2826 wtf("Exception in mReceiver.onReceive", e); 2827 } 2828 } 2829 }; 2830 handleLocaleChanged()2831 void handleLocaleChanged() { 2832 if (DEBUG) { 2833 Slog.d(TAG, "handleLocaleChanged"); 2834 } 2835 scheduleSaveBaseState(); 2836 2837 synchronized (mLock) { 2838 final long token = injectClearCallingIdentity(); 2839 try { 2840 forEachLoadedUserLocked(user -> user.detectLocaleChange()); 2841 } finally { 2842 injectRestoreCallingIdentity(token); 2843 } 2844 } 2845 } 2846 2847 /** 2848 * Package event callbacks. 2849 */ 2850 @VisibleForTesting 2851 final BroadcastReceiver mPackageMonitor = new BroadcastReceiver() { 2852 @Override 2853 public void onReceive(Context context, Intent intent) { 2854 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 2855 if (userId == UserHandle.USER_NULL) { 2856 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent); 2857 return; 2858 } 2859 2860 final String action = intent.getAction(); 2861 2862 // This is normally called on Handler, so clearCallingIdentity() isn't needed, 2863 // but we still check it in unit tests. 2864 final long token = injectClearCallingIdentity(); 2865 try { 2866 synchronized (mLock) { 2867 if (!isUserUnlockedL(userId)) { 2868 if (DEBUG) { 2869 Slog.d(TAG, "Ignoring package broadcast " + action 2870 + " for locked/stopped user " + userId); 2871 } 2872 return; 2873 } 2874 2875 // Whenever we get one of those package broadcasts, or get 2876 // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache. 2877 final ShortcutUser user = getUserShortcutsLocked(userId); 2878 user.clearLauncher(); 2879 } 2880 if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) { 2881 // Nothing farther to do. 2882 return; 2883 } 2884 2885 final Uri intentUri = intent.getData(); 2886 final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() 2887 : null; 2888 if (packageName == null) { 2889 Slog.w(TAG, "Intent broadcast does not contain package name: " + intent); 2890 return; 2891 } 2892 2893 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 2894 2895 switch (action) { 2896 case Intent.ACTION_PACKAGE_ADDED: 2897 if (replacing) { 2898 handlePackageUpdateFinished(packageName, userId); 2899 } else { 2900 handlePackageAdded(packageName, userId); 2901 } 2902 break; 2903 case Intent.ACTION_PACKAGE_REMOVED: 2904 if (!replacing) { 2905 handlePackageRemoved(packageName, userId); 2906 } 2907 break; 2908 case Intent.ACTION_PACKAGE_CHANGED: 2909 handlePackageChanged(packageName, userId); 2910 2911 break; 2912 case Intent.ACTION_PACKAGE_DATA_CLEARED: 2913 handlePackageDataCleared(packageName, userId); 2914 break; 2915 } 2916 } catch (Exception e) { 2917 wtf("Exception in mPackageMonitor.onReceive", e); 2918 } finally { 2919 injectRestoreCallingIdentity(token); 2920 } 2921 } 2922 }; 2923 2924 /** 2925 * Called when a user is unlocked. 2926 * - Check all known packages still exist, and otherwise perform cleanup. 2927 * - If a package still exists, check the version code. If it's been updated, may need to 2928 * update timestamps of its shortcuts. 2929 */ 2930 @VisibleForTesting checkPackageChanges(@serIdInt int ownerUserId)2931 void checkPackageChanges(@UserIdInt int ownerUserId) { 2932 if (DEBUG) { 2933 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId); 2934 } 2935 if (injectIsSafeModeEnabled()) { 2936 Slog.i(TAG, "Safe mode, skipping checkPackageChanges()"); 2937 return; 2938 } 2939 2940 final long start = getStatStartTime(); 2941 try { 2942 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>(); 2943 2944 synchronized (mLock) { 2945 final ShortcutUser user = getUserShortcutsLocked(ownerUserId); 2946 2947 // Find packages that have been uninstalled. 2948 user.forAllPackageItems(spi -> { 2949 if (spi.getPackageInfo().isShadow()) { 2950 return; // Don't delete shadow information. 2951 } 2952 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) { 2953 if (DEBUG) { 2954 Slog.d(TAG, "Uninstalled: " + spi.getPackageName() 2955 + " user " + spi.getPackageUserId()); 2956 } 2957 gonePackages.add(PackageWithUser.of(spi)); 2958 } 2959 }); 2960 if (gonePackages.size() > 0) { 2961 for (int i = gonePackages.size() - 1; i >= 0; i--) { 2962 final PackageWithUser pu = gonePackages.get(i); 2963 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId, 2964 /* appStillExists = */ false); 2965 } 2966 } 2967 2968 rescanUpdatedPackagesLocked(ownerUserId, user.getLastAppScanTime()); 2969 } 2970 } finally { 2971 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start); 2972 } 2973 verifyStates(); 2974 } 2975 2976 @GuardedBy("mLock") rescanUpdatedPackagesLocked(@serIdInt int userId, long lastScanTime)2977 private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) { 2978 final ShortcutUser user = getUserShortcutsLocked(userId); 2979 2980 // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime 2981 // is not reliable. 2982 final long now = injectCurrentTimeMillis(); 2983 final boolean afterOta = 2984 !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint()); 2985 2986 // Then for each installed app, publish manifest shortcuts when needed. 2987 forUpdatedPackages(userId, lastScanTime, afterOta, ai -> { 2988 user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId); 2989 2990 user.rescanPackageIfNeeded(ai.packageName, /* forceRescan= */ true); 2991 }); 2992 2993 // Write the time just before the scan, because there may be apps that have just 2994 // been updated, and we want to catch them in the next time. 2995 user.setLastAppScanTime(now); 2996 user.setLastAppScanOsFingerprint(injectBuildFingerprint()); 2997 scheduleSaveUser(userId); 2998 } 2999 handlePackageAdded(String packageName, @UserIdInt int userId)3000 private void handlePackageAdded(String packageName, @UserIdInt int userId) { 3001 if (DEBUG) { 3002 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId)); 3003 } 3004 synchronized (mLock) { 3005 final ShortcutUser user = getUserShortcutsLocked(userId); 3006 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 3007 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true); 3008 } 3009 verifyStates(); 3010 } 3011 handlePackageUpdateFinished(String packageName, @UserIdInt int userId)3012 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) { 3013 if (DEBUG) { 3014 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d", 3015 packageName, userId)); 3016 } 3017 synchronized (mLock) { 3018 final ShortcutUser user = getUserShortcutsLocked(userId); 3019 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 3020 3021 if (isPackageInstalled(packageName, userId)) { 3022 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true); 3023 } 3024 } 3025 verifyStates(); 3026 } 3027 handlePackageRemoved(String packageName, @UserIdInt int packageUserId)3028 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) { 3029 if (DEBUG) { 3030 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, 3031 packageUserId)); 3032 } 3033 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false); 3034 3035 verifyStates(); 3036 } 3037 handlePackageDataCleared(String packageName, int packageUserId)3038 private void handlePackageDataCleared(String packageName, int packageUserId) { 3039 if (DEBUG) { 3040 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName, 3041 packageUserId)); 3042 } 3043 cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true); 3044 3045 verifyStates(); 3046 } 3047 handlePackageChanged(String packageName, int packageUserId)3048 private void handlePackageChanged(String packageName, int packageUserId) { 3049 if (!isPackageInstalled(packageName, packageUserId)) { 3050 // Probably disabled, which is the same thing as uninstalled. 3051 handlePackageRemoved(packageName, packageUserId); 3052 return; 3053 } 3054 if (DEBUG) { 3055 Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName, 3056 packageUserId)); 3057 } 3058 3059 // Activities may be disabled or enabled. Just rescan the package. 3060 synchronized (mLock) { 3061 final ShortcutUser user = getUserShortcutsLocked(packageUserId); 3062 3063 user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true); 3064 } 3065 3066 verifyStates(); 3067 } 3068 3069 // === PackageManager interaction === 3070 3071 /** 3072 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 3073 */ 3074 @Nullable getPackageInfoWithSignatures(String packageName, @UserIdInt int userId)3075 final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { 3076 return getPackageInfo(packageName, userId, true); 3077 } 3078 3079 /** 3080 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 3081 */ 3082 @Nullable getPackageInfo(String packageName, @UserIdInt int userId)3083 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { 3084 return getPackageInfo(packageName, userId, false); 3085 } 3086 injectGetPackageUid(@onNull String packageName, @UserIdInt int userId)3087 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { 3088 final long token = injectClearCallingIdentity(); 3089 try { 3090 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId); 3091 } catch (RemoteException e) { 3092 // Shouldn't happen. 3093 Slog.wtf(TAG, "RemoteException", e); 3094 return -1; 3095 } finally { 3096 injectRestoreCallingIdentity(token); 3097 } 3098 } 3099 3100 /** 3101 * Returns {@link PackageInfo} unless it's uninstalled or disabled. 3102 */ 3103 @Nullable 3104 @VisibleForTesting getPackageInfo(String packageName, @UserIdInt int userId, boolean getSignatures)3105 final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId, 3106 boolean getSignatures) { 3107 return isInstalledOrNull(injectPackageInfoWithUninstalled( 3108 packageName, userId, getSignatures)); 3109 } 3110 3111 /** 3112 * Do not use directly; this returns uninstalled packages too. 3113 */ 3114 @Nullable 3115 @VisibleForTesting injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId, boolean getSignatures)3116 PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId, 3117 boolean getSignatures) { 3118 final long start = getStatStartTime(); 3119 final long token = injectClearCallingIdentity(); 3120 try { 3121 return mIPackageManager.getPackageInfo( 3122 packageName, PACKAGE_MATCH_FLAGS 3123 | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0), 3124 userId); 3125 } catch (RemoteException e) { 3126 // Shouldn't happen. 3127 Slog.wtf(TAG, "RemoteException", e); 3128 return null; 3129 } finally { 3130 injectRestoreCallingIdentity(token); 3131 3132 logDurationStat( 3133 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO), 3134 start); 3135 } 3136 } 3137 3138 /** 3139 * Returns {@link ApplicationInfo} unless it's uninstalled or disabled. 3140 */ 3141 @Nullable 3142 @VisibleForTesting getApplicationInfo(String packageName, @UserIdInt int userId)3143 final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) { 3144 return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId)); 3145 } 3146 3147 /** 3148 * Do not use directly; this returns uninstalled packages too. 3149 */ 3150 @Nullable 3151 @VisibleForTesting injectApplicationInfoWithUninstalled( String packageName, @UserIdInt int userId)3152 ApplicationInfo injectApplicationInfoWithUninstalled( 3153 String packageName, @UserIdInt int userId) { 3154 final long start = getStatStartTime(); 3155 final long token = injectClearCallingIdentity(); 3156 try { 3157 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); 3158 } catch (RemoteException e) { 3159 // Shouldn't happen. 3160 Slog.wtf(TAG, "RemoteException", e); 3161 return null; 3162 } finally { 3163 injectRestoreCallingIdentity(token); 3164 3165 logDurationStat(Stats.GET_APPLICATION_INFO, start); 3166 } 3167 } 3168 3169 /** 3170 * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled. 3171 */ 3172 @Nullable getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId)3173 final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) { 3174 return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled( 3175 activity, userId)); 3176 } 3177 3178 /** 3179 * Do not use directly; this returns uninstalled packages too. 3180 */ 3181 @Nullable 3182 @VisibleForTesting injectGetActivityInfoWithMetadataWithUninstalled( ComponentName activity, @UserIdInt int userId)3183 ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled( 3184 ComponentName activity, @UserIdInt int userId) { 3185 final long start = getStatStartTime(); 3186 final long token = injectClearCallingIdentity(); 3187 try { 3188 return mIPackageManager.getActivityInfo(activity, 3189 (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId); 3190 } catch (RemoteException e) { 3191 // Shouldn't happen. 3192 Slog.wtf(TAG, "RemoteException", e); 3193 return null; 3194 } finally { 3195 injectRestoreCallingIdentity(token); 3196 3197 logDurationStat(Stats.GET_ACTIVITY_WITH_METADATA, start); 3198 } 3199 } 3200 3201 /** 3202 * Return all installed and enabled packages. 3203 */ 3204 @NonNull 3205 @VisibleForTesting getInstalledPackages(@serIdInt int userId)3206 final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) { 3207 final long start = getStatStartTime(); 3208 final long token = injectClearCallingIdentity(); 3209 try { 3210 final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId); 3211 3212 all.removeIf(PACKAGE_NOT_INSTALLED); 3213 3214 return all; 3215 } catch (RemoteException e) { 3216 // Shouldn't happen. 3217 Slog.wtf(TAG, "RemoteException", e); 3218 return null; 3219 } finally { 3220 injectRestoreCallingIdentity(token); 3221 3222 logDurationStat(Stats.GET_INSTALLED_PACKAGES, start); 3223 } 3224 } 3225 3226 /** 3227 * Do not use directly; this returns uninstalled packages too. 3228 */ 3229 @NonNull 3230 @VisibleForTesting injectGetPackagesWithUninstalled(@serIdInt int userId)3231 List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId) 3232 throws RemoteException { 3233 final ParceledListSlice<PackageInfo> parceledList = 3234 mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId); 3235 if (parceledList == null) { 3236 return Collections.emptyList(); 3237 } 3238 return parceledList.getList(); 3239 } 3240 forUpdatedPackages(@serIdInt int userId, long lastScanTime, boolean afterOta, Consumer<ApplicationInfo> callback)3241 private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta, 3242 Consumer<ApplicationInfo> callback) { 3243 if (DEBUG) { 3244 Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime 3245 + " afterOta=" + afterOta); 3246 } 3247 final List<PackageInfo> list = getInstalledPackages(userId); 3248 for (int i = list.size() - 1; i >= 0; i--) { 3249 final PackageInfo pi = list.get(i); 3250 3251 // If the package has been updated since the last scan time, then scan it. 3252 // Also if it's right after an OTA, always re-scan all apps anyway, since the 3253 // shortcut parser might have changed. 3254 if (afterOta || (pi.lastUpdateTime >= lastScanTime)) { 3255 if (DEBUG) { 3256 Slog.d(TAG, "Found updated package " + pi.packageName 3257 + " updateTime=" + pi.lastUpdateTime); 3258 } 3259 callback.accept(pi.applicationInfo); 3260 } 3261 } 3262 } 3263 isApplicationFlagSet(@onNull String packageName, int userId, int flags)3264 private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) { 3265 final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId); 3266 return (ai != null) && ((ai.flags & flags) == flags); 3267 } 3268 isInstalled(@ullable ApplicationInfo ai)3269 private static boolean isInstalled(@Nullable ApplicationInfo ai) { 3270 return (ai != null) && ai.enabled && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 3271 } 3272 isEphemeralApp(@ullable ApplicationInfo ai)3273 private static boolean isEphemeralApp(@Nullable ApplicationInfo ai) { 3274 return (ai != null) && ai.isInstantApp(); 3275 } 3276 isInstalled(@ullable PackageInfo pi)3277 private static boolean isInstalled(@Nullable PackageInfo pi) { 3278 return (pi != null) && isInstalled(pi.applicationInfo); 3279 } 3280 isInstalled(@ullable ActivityInfo ai)3281 private static boolean isInstalled(@Nullable ActivityInfo ai) { 3282 return (ai != null) && isInstalled(ai.applicationInfo); 3283 } 3284 isInstalledOrNull(ApplicationInfo ai)3285 private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) { 3286 return isInstalled(ai) ? ai : null; 3287 } 3288 isInstalledOrNull(PackageInfo pi)3289 private static PackageInfo isInstalledOrNull(PackageInfo pi) { 3290 return isInstalled(pi) ? pi : null; 3291 } 3292 isInstalledOrNull(ActivityInfo ai)3293 private static ActivityInfo isInstalledOrNull(ActivityInfo ai) { 3294 return isInstalled(ai) ? ai : null; 3295 } 3296 isPackageInstalled(String packageName, int userId)3297 boolean isPackageInstalled(String packageName, int userId) { 3298 return getApplicationInfo(packageName, userId) != null; 3299 } 3300 isEphemeralApp(String packageName, int userId)3301 boolean isEphemeralApp(String packageName, int userId) { 3302 return isEphemeralApp(getApplicationInfo(packageName, userId)); 3303 } 3304 3305 @Nullable injectXmlMetaData(ActivityInfo activityInfo, String key)3306 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) { 3307 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key); 3308 } 3309 3310 @Nullable injectGetResourcesForApplicationAsUser(String packageName, int userId)3311 Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) { 3312 final long start = getStatStartTime(); 3313 final long token = injectClearCallingIdentity(); 3314 try { 3315 return mContext.getPackageManager().getResourcesForApplicationAsUser( 3316 packageName, userId); 3317 } catch (NameNotFoundException e) { 3318 Slog.e(TAG, "Resources for package " + packageName + " not found"); 3319 return null; 3320 } finally { 3321 injectRestoreCallingIdentity(token); 3322 3323 logDurationStat(Stats.GET_APPLICATION_RESOURCES, start); 3324 } 3325 } 3326 getMainActivityIntent()3327 private Intent getMainActivityIntent() { 3328 final Intent intent = new Intent(Intent.ACTION_MAIN); 3329 intent.addCategory(LAUNCHER_INTENT_CATEGORY); 3330 return intent; 3331 } 3332 3333 /** 3334 * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed, 3335 * and only returns exported activities. 3336 */ 3337 @NonNull 3338 @VisibleForTesting queryActivities(@onNull Intent baseIntent, @NonNull String packageName, @Nullable ComponentName activity, int userId)3339 List<ResolveInfo> queryActivities(@NonNull Intent baseIntent, 3340 @NonNull String packageName, @Nullable ComponentName activity, int userId) { 3341 3342 baseIntent.setPackage(Preconditions.checkNotNull(packageName)); 3343 if (activity != null) { 3344 baseIntent.setComponent(activity); 3345 } 3346 return queryActivities(baseIntent, userId, /* exportedOnly =*/ true); 3347 } 3348 3349 @NonNull queryActivities(@onNull Intent intent, int userId, boolean exportedOnly)3350 List<ResolveInfo> queryActivities(@NonNull Intent intent, int userId, 3351 boolean exportedOnly) { 3352 final List<ResolveInfo> resolved; 3353 final long token = injectClearCallingIdentity(); 3354 try { 3355 resolved = 3356 mContext.getPackageManager().queryIntentActivitiesAsUser( 3357 intent, PACKAGE_MATCH_FLAGS, userId); 3358 } finally { 3359 injectRestoreCallingIdentity(token); 3360 } 3361 if (resolved == null || resolved.size() == 0) { 3362 return EMPTY_RESOLVE_INFO; 3363 } 3364 // Make sure the package is installed. 3365 if (!isInstalled(resolved.get(0).activityInfo)) { 3366 return EMPTY_RESOLVE_INFO; 3367 } 3368 if (exportedOnly) { 3369 resolved.removeIf(ACTIVITY_NOT_EXPORTED); 3370 } 3371 return resolved; 3372 } 3373 3374 /** 3375 * Return the main activity that is enabled and exported. If multiple activities are found, 3376 * return the first one. 3377 */ 3378 @Nullable injectGetDefaultMainActivity(@onNull String packageName, int userId)3379 ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) { 3380 final long start = getStatStartTime(); 3381 try { 3382 final List<ResolveInfo> resolved = 3383 queryActivities(getMainActivityIntent(), packageName, null, userId); 3384 return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName(); 3385 } finally { 3386 logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start); 3387 } 3388 } 3389 3390 /** 3391 * Return whether an activity is enabled, exported and main. 3392 */ injectIsMainActivity(@onNull ComponentName activity, int userId)3393 boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) { 3394 final long start = getStatStartTime(); 3395 try { 3396 if (activity == null) { 3397 wtf("null activity detected"); 3398 return false; 3399 } 3400 if (DUMMY_MAIN_ACTIVITY.equals(activity.getClassName())) { 3401 return true; 3402 } 3403 final List<ResolveInfo> resolved = queryActivities( 3404 getMainActivityIntent(), activity.getPackageName(), activity, userId); 3405 return resolved.size() > 0; 3406 } finally { 3407 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); 3408 } 3409 } 3410 3411 /** 3412 * Create a dummy "main activity" component name which is used to create a dynamic shortcut 3413 * with no main activity temporarily. 3414 */ 3415 @NonNull getDummyMainActivity(@onNull String packageName)3416 ComponentName getDummyMainActivity(@NonNull String packageName) { 3417 return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY); 3418 } 3419 isDummyMainActivity(@ullable ComponentName name)3420 boolean isDummyMainActivity(@Nullable ComponentName name) { 3421 return name != null && DUMMY_MAIN_ACTIVITY.equals(name.getClassName()); 3422 } 3423 3424 /** 3425 * Return all the enabled, exported and main activities from a package. 3426 */ 3427 @NonNull injectGetMainActivities(@onNull String packageName, int userId)3428 List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) { 3429 final long start = getStatStartTime(); 3430 try { 3431 return queryActivities(getMainActivityIntent(), packageName, null, userId); 3432 } finally { 3433 logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); 3434 } 3435 } 3436 3437 /** 3438 * Return whether an activity is enabled and exported. 3439 */ 3440 @VisibleForTesting injectIsActivityEnabledAndExported( @onNull ComponentName activity, @UserIdInt int userId)3441 boolean injectIsActivityEnabledAndExported( 3442 @NonNull ComponentName activity, @UserIdInt int userId) { 3443 final long start = getStatStartTime(); 3444 try { 3445 return queryActivities(new Intent(), activity.getPackageName(), activity, userId) 3446 .size() > 0; 3447 } finally { 3448 logDurationStat(Stats.IS_ACTIVITY_ENABLED, start); 3449 } 3450 } 3451 3452 /** 3453 * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} or 3454 * {@link LauncherApps#ACTION_CONFIRM_PIN_APPWIDGET} activity in a given package depending on 3455 * the requestType. 3456 */ 3457 @Nullable injectGetPinConfirmationActivity(@onNull String launcherPackageName, int launcherUserId, int requestType)3458 ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName, 3459 int launcherUserId, int requestType) { 3460 Preconditions.checkNotNull(launcherPackageName); 3461 String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ? 3462 LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT : 3463 LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET; 3464 3465 final Intent confirmIntent = new Intent(action).setPackage(launcherPackageName); 3466 final List<ResolveInfo> candidates = queryActivities( 3467 confirmIntent, launcherUserId, /* exportedOnly =*/ false); 3468 for (ResolveInfo ri : candidates) { 3469 return ri.activityInfo.getComponentName(); 3470 } 3471 return null; 3472 } 3473 injectIsSafeModeEnabled()3474 boolean injectIsSafeModeEnabled() { 3475 final long token = injectClearCallingIdentity(); 3476 try { 3477 return IWindowManager.Stub 3478 .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)) 3479 .isSafeModeEnabled(); 3480 } catch (RemoteException e) { 3481 return false; // Shouldn't happen though. 3482 } finally { 3483 injectRestoreCallingIdentity(token); 3484 } 3485 } 3486 3487 /** 3488 * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return 3489 * itself. 3490 */ getParentOrSelfUserId(int userId)3491 int getParentOrSelfUserId(int userId) { 3492 return mUserManagerInternal.getProfileParentId(userId); 3493 } 3494 injectSendIntentSender(IntentSender intentSender, Intent extras)3495 void injectSendIntentSender(IntentSender intentSender, Intent extras) { 3496 if (intentSender == null) { 3497 return; 3498 } 3499 try { 3500 intentSender.sendIntent(mContext, /* code= */ 0, extras, 3501 /* onFinished=*/ null, /* handler= */ null); 3502 } catch (SendIntentException e) { 3503 Slog.w(TAG, "sendIntent failed().", e); 3504 } 3505 } 3506 3507 // === Backup & restore === 3508 shouldBackupApp(String packageName, int userId)3509 boolean shouldBackupApp(String packageName, int userId) { 3510 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP); 3511 } 3512 shouldBackupApp(PackageInfo pi)3513 static boolean shouldBackupApp(PackageInfo pi) { 3514 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; 3515 } 3516 3517 @Override getBackupPayload(@serIdInt int userId)3518 public byte[] getBackupPayload(@UserIdInt int userId) { 3519 enforceSystem(); 3520 if (DEBUG) { 3521 Slog.d(TAG, "Backing up user " + userId); 3522 } 3523 synchronized (mLock) { 3524 if (!isUserUnlockedL(userId)) { 3525 wtf("Can't backup: user " + userId + " is locked or not running"); 3526 return null; 3527 } 3528 3529 final ShortcutUser user = getUserShortcutsLocked(userId); 3530 if (user == null) { 3531 wtf("Can't backup: user not found: id=" + userId); 3532 return null; 3533 } 3534 3535 // Update the signatures for all packages. 3536 user.forAllPackageItems(spi -> spi.refreshPackageSignatureAndSave()); 3537 3538 // Rescan all apps; this will also update the version codes and "allow-backup". 3539 user.forAllPackages(pkg -> pkg.rescanPackageIfNeeded( 3540 /*isNewApp=*/ false, /*forceRescan=*/ true)); 3541 3542 // Set the version code for the launchers. 3543 user.forAllLaunchers(launcher -> launcher.ensurePackageInfo()); 3544 3545 // Save to the filesystem. 3546 scheduleSaveUser(userId); 3547 saveDirtyInfo(); 3548 3549 // Note, in case of backup, we don't have to wait on bitmap saving, because we don't 3550 // back up bitmaps anyway. 3551 3552 // Then create the backup payload. 3553 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024); 3554 try { 3555 saveUserInternalLocked(userId, os, /* forBackup */ true); 3556 } catch (XmlPullParserException | IOException e) { 3557 // Shouldn't happen. 3558 Slog.w(TAG, "Backup failed.", e); 3559 return null; 3560 } 3561 byte[] payload = os.toByteArray(); 3562 mShortcutDumpFiles.save("backup-1-payload.txt", payload); 3563 return payload; 3564 } 3565 } 3566 3567 @Override applyRestore(byte[] payload, @UserIdInt int userId)3568 public void applyRestore(byte[] payload, @UserIdInt int userId) { 3569 enforceSystem(); 3570 if (DEBUG) { 3571 Slog.d(TAG, "Restoring user " + userId); 3572 } 3573 synchronized (mLock) { 3574 if (!isUserUnlockedL(userId)) { 3575 wtf("Can't restore: user " + userId + " is locked or not running"); 3576 return; 3577 } 3578 3579 // Note we print the file timestamps in dumpsys too, but also printing the timestamp 3580 // in the files anyway. 3581 mShortcutDumpFiles.save("restore-0-start.txt", pw -> { 3582 pw.print("Start time: "); 3583 dumpCurrentTime(pw); 3584 pw.println(); 3585 }); 3586 mShortcutDumpFiles.save("restore-1-payload.xml", payload); 3587 3588 // Actually do restore. 3589 final ShortcutUser restored; 3590 final ByteArrayInputStream is = new ByteArrayInputStream(payload); 3591 try { 3592 restored = loadUserInternal(userId, is, /* fromBackup */ true); 3593 } catch (XmlPullParserException | IOException | InvalidFileFormatException e) { 3594 Slog.w(TAG, "Restoration failed.", e); 3595 return; 3596 } 3597 mShortcutDumpFiles.save("restore-2.txt", this::dumpInner); 3598 3599 getUserShortcutsLocked(userId).mergeRestoredFile(restored); 3600 3601 mShortcutDumpFiles.save("restore-3.txt", this::dumpInner); 3602 3603 // Rescan all packages to re-publish manifest shortcuts and do other checks. 3604 rescanUpdatedPackagesLocked(userId, 3605 0 // lastScanTime = 0; rescan all packages. 3606 ); 3607 3608 mShortcutDumpFiles.save("restore-4.txt", this::dumpInner); 3609 3610 mShortcutDumpFiles.save("restore-5-finish.txt", pw -> { 3611 pw.print("Finish time: "); 3612 dumpCurrentTime(pw); 3613 pw.println(); 3614 }); 3615 3616 saveUserLocked(userId); 3617 } 3618 } 3619 3620 // === Dump === 3621 3622 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)3623 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3624 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; 3625 dumpNoCheck(fd, pw, args); 3626 } 3627 3628 @VisibleForTesting dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args)3629 void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) { 3630 final DumpFilter filter = parseDumpArgs(args); 3631 3632 if (filter.shouldDumpCheckIn()) { 3633 // Other flags are not supported for checkin. 3634 dumpCheckin(pw, filter.shouldCheckInClear()); 3635 } else { 3636 if (filter.shouldDumpMain()) { 3637 dumpInner(pw, filter); 3638 pw.println(); 3639 } 3640 if (filter.shouldDumpUid()) { 3641 dumpUid(pw); 3642 pw.println(); 3643 } 3644 if (filter.shouldDumpFiles()) { 3645 dumpDumpFiles(pw); 3646 pw.println(); 3647 } 3648 } 3649 } 3650 parseDumpArgs(String[] args)3651 private static DumpFilter parseDumpArgs(String[] args) { 3652 final DumpFilter filter = new DumpFilter(); 3653 if (args == null) { 3654 return filter; 3655 } 3656 3657 int argIndex = 0; 3658 while (argIndex < args.length) { 3659 final String arg = args[argIndex++]; 3660 3661 if ("-c".equals(arg)) { 3662 filter.setDumpCheckIn(true); 3663 continue; 3664 } 3665 if ("--checkin".equals(arg)) { 3666 filter.setDumpCheckIn(true); 3667 filter.setCheckInClear(true); 3668 continue; 3669 } 3670 if ("-a".equals(arg) || "--all".equals(arg)) { 3671 filter.setDumpUid(true); 3672 filter.setDumpFiles(true); 3673 continue; 3674 } 3675 if ("-u".equals(arg) || "--uid".equals(arg)) { 3676 filter.setDumpUid(true); 3677 continue; 3678 } 3679 if ("-f".equals(arg) || "--files".equals(arg)) { 3680 filter.setDumpFiles(true); 3681 continue; 3682 } 3683 if ("-n".equals(arg) || "--no-main".equals(arg)) { 3684 filter.setDumpMain(false); 3685 continue; 3686 } 3687 if ("--user".equals(arg)) { 3688 if (argIndex >= args.length) { 3689 throw new IllegalArgumentException("Missing user ID for --user"); 3690 } 3691 try { 3692 filter.addUser(Integer.parseInt(args[argIndex++])); 3693 } catch (NumberFormatException e) { 3694 throw new IllegalArgumentException("Invalid user ID", e); 3695 } 3696 continue; 3697 } 3698 if ("-p".equals(arg) || "--package".equals(arg)) { 3699 if (argIndex >= args.length) { 3700 throw new IllegalArgumentException("Missing package name for --package"); 3701 } 3702 filter.addPackageRegex(args[argIndex++]); 3703 filter.setDumpDetails(false); 3704 continue; 3705 } 3706 if (arg.startsWith("-")) { 3707 throw new IllegalArgumentException("Unknown option " + arg); 3708 } 3709 break; 3710 } 3711 while (argIndex < args.length) { 3712 filter.addPackage(args[argIndex++]); 3713 } 3714 return filter; 3715 } 3716 3717 static class DumpFilter { 3718 private boolean mDumpCheckIn = false; 3719 private boolean mCheckInClear = false; 3720 3721 private boolean mDumpMain = true; 3722 private boolean mDumpUid = false; 3723 private boolean mDumpFiles = false; 3724 3725 private boolean mDumpDetails = true; 3726 private List<Pattern> mPackagePatterns = new ArrayList<>(); 3727 private List<Integer> mUsers = new ArrayList<>(); 3728 addPackageRegex(String regex)3729 void addPackageRegex(String regex) { 3730 mPackagePatterns.add(Pattern.compile(regex)); 3731 } 3732 addPackage(String packageName)3733 public void addPackage(String packageName) { 3734 addPackageRegex(Pattern.quote(packageName)); 3735 } 3736 addUser(int userId)3737 void addUser(int userId) { 3738 mUsers.add(userId); 3739 } 3740 isPackageMatch(String packageName)3741 boolean isPackageMatch(String packageName) { 3742 if (mPackagePatterns.size() == 0) { 3743 return true; 3744 } 3745 for (int i = 0; i < mPackagePatterns.size(); i++) { 3746 if (mPackagePatterns.get(i).matcher(packageName).find()) { 3747 return true; 3748 } 3749 } 3750 return false; 3751 } 3752 isUserMatch(int userId)3753 boolean isUserMatch(int userId) { 3754 if (mUsers.size() == 0) { 3755 return true; 3756 } 3757 for (int i = 0; i < mUsers.size(); i++) { 3758 if (mUsers.get(i) == userId) { 3759 return true; 3760 } 3761 } 3762 return false; 3763 } 3764 shouldDumpCheckIn()3765 public boolean shouldDumpCheckIn() { 3766 return mDumpCheckIn; 3767 } 3768 setDumpCheckIn(boolean dumpCheckIn)3769 public void setDumpCheckIn(boolean dumpCheckIn) { 3770 mDumpCheckIn = dumpCheckIn; 3771 } 3772 shouldCheckInClear()3773 public boolean shouldCheckInClear() { 3774 return mCheckInClear; 3775 } 3776 setCheckInClear(boolean checkInClear)3777 public void setCheckInClear(boolean checkInClear) { 3778 mCheckInClear = checkInClear; 3779 } 3780 shouldDumpMain()3781 public boolean shouldDumpMain() { 3782 return mDumpMain; 3783 } 3784 setDumpMain(boolean dumpMain)3785 public void setDumpMain(boolean dumpMain) { 3786 mDumpMain = dumpMain; 3787 } 3788 shouldDumpUid()3789 public boolean shouldDumpUid() { 3790 return mDumpUid; 3791 } 3792 setDumpUid(boolean dumpUid)3793 public void setDumpUid(boolean dumpUid) { 3794 mDumpUid = dumpUid; 3795 } 3796 shouldDumpFiles()3797 public boolean shouldDumpFiles() { 3798 return mDumpFiles; 3799 } 3800 setDumpFiles(boolean dumpFiles)3801 public void setDumpFiles(boolean dumpFiles) { 3802 mDumpFiles = dumpFiles; 3803 } 3804 shouldDumpDetails()3805 public boolean shouldDumpDetails() { 3806 return mDumpDetails; 3807 } 3808 setDumpDetails(boolean dumpDetails)3809 public void setDumpDetails(boolean dumpDetails) { 3810 mDumpDetails = dumpDetails; 3811 } 3812 } 3813 dumpInner(PrintWriter pw)3814 private void dumpInner(PrintWriter pw) { 3815 dumpInner(pw, new DumpFilter()); 3816 } 3817 dumpInner(PrintWriter pw, DumpFilter filter)3818 private void dumpInner(PrintWriter pw, DumpFilter filter) { 3819 synchronized (mLock) { 3820 if (filter.shouldDumpDetails()) { 3821 final long now = injectCurrentTimeMillis(); 3822 pw.print("Now: ["); 3823 pw.print(now); 3824 pw.print("] "); 3825 pw.print(formatTime(now)); 3826 3827 pw.print(" Raw last reset: ["); 3828 pw.print(mRawLastResetTime); 3829 pw.print("] "); 3830 pw.print(formatTime(mRawLastResetTime)); 3831 3832 final long last = getLastResetTimeLocked(); 3833 pw.print(" Last reset: ["); 3834 pw.print(last); 3835 pw.print("] "); 3836 pw.print(formatTime(last)); 3837 3838 final long next = getNextResetTimeLocked(); 3839 pw.print(" Next reset: ["); 3840 pw.print(next); 3841 pw.print("] "); 3842 pw.print(formatTime(next)); 3843 pw.println(); 3844 pw.println(); 3845 3846 pw.print(" Config:"); 3847 pw.print(" Max icon dim: "); 3848 pw.println(mMaxIconDimension); 3849 pw.print(" Icon format: "); 3850 pw.println(mIconPersistFormat); 3851 pw.print(" Icon quality: "); 3852 pw.println(mIconPersistQuality); 3853 pw.print(" saveDelayMillis: "); 3854 pw.println(mSaveDelayMillis); 3855 pw.print(" resetInterval: "); 3856 pw.println(mResetInterval); 3857 pw.print(" maxUpdatesPerInterval: "); 3858 pw.println(mMaxUpdatesPerInterval); 3859 pw.print(" maxShortcutsPerActivity: "); 3860 pw.println(mMaxShortcuts); 3861 pw.println(); 3862 3863 mStatLogger.dump(pw, " "); 3864 3865 pw.println(); 3866 pw.print(" #Failures: "); 3867 pw.println(mWtfCount); 3868 3869 if (mLastWtfStacktrace != null) { 3870 pw.print(" Last failure stack trace: "); 3871 pw.println(Log.getStackTraceString(mLastWtfStacktrace)); 3872 } 3873 3874 pw.println(); 3875 mShortcutBitmapSaver.dumpLocked(pw, " "); 3876 3877 pw.println(); 3878 } 3879 3880 for (int i = 0; i < mUsers.size(); i++) { 3881 final ShortcutUser user = mUsers.valueAt(i); 3882 if (filter.isUserMatch(user.getUserId())) { 3883 user.dump(pw, " ", filter); 3884 pw.println(); 3885 } 3886 } 3887 3888 for (int i = 0; i < mShortcutNonPersistentUsers.size(); i++) { 3889 final ShortcutNonPersistentUser user = mShortcutNonPersistentUsers.valueAt(i); 3890 if (filter.isUserMatch(user.getUserId())) { 3891 user.dump(pw, " ", filter); 3892 pw.println(); 3893 } 3894 } 3895 } 3896 } 3897 dumpUid(PrintWriter pw)3898 private void dumpUid(PrintWriter pw) { 3899 synchronized (mLock) { 3900 pw.println("** SHORTCUT MANAGER UID STATES (dumpsys shortcut -n -u)"); 3901 3902 for (int i = 0; i < mUidState.size(); i++) { 3903 final int uid = mUidState.keyAt(i); 3904 final int state = mUidState.valueAt(i); 3905 pw.print(" UID="); 3906 pw.print(uid); 3907 pw.print(" state="); 3908 pw.print(state); 3909 if (isProcessStateForeground(state)) { 3910 pw.print(" [FG]"); 3911 } 3912 pw.print(" last FG="); 3913 pw.print(mUidLastForegroundElapsedTime.get(uid)); 3914 pw.println(); 3915 } 3916 } 3917 } 3918 formatTime(long time)3919 static String formatTime(long time) { 3920 Time tobj = new Time(); 3921 tobj.set(time); 3922 return tobj.format("%Y-%m-%d %H:%M:%S"); 3923 } 3924 dumpCurrentTime(PrintWriter pw)3925 private void dumpCurrentTime(PrintWriter pw) { 3926 pw.print(formatTime(injectCurrentTimeMillis())); 3927 } 3928 3929 /** 3930 * Dumpsys for checkin. 3931 * 3932 * @param clear if true, clear the history information. Some other system services have this 3933 * behavior but shortcut service doesn't for now. 3934 */ dumpCheckin(PrintWriter pw, boolean clear)3935 private void dumpCheckin(PrintWriter pw, boolean clear) { 3936 synchronized (mLock) { 3937 try { 3938 final JSONArray users = new JSONArray(); 3939 3940 for (int i = 0; i < mUsers.size(); i++) { 3941 users.put(mUsers.valueAt(i).dumpCheckin(clear)); 3942 } 3943 3944 final JSONObject result = new JSONObject(); 3945 3946 result.put(KEY_SHORTCUT, users); 3947 result.put(KEY_LOW_RAM, injectIsLowRamDevice()); 3948 result.put(KEY_ICON_SIZE, mMaxIconDimension); 3949 3950 pw.println(result.toString(1)); 3951 } catch (JSONException e) { 3952 Slog.e(TAG, "Unable to write in json", e); 3953 } 3954 } 3955 } 3956 dumpDumpFiles(PrintWriter pw)3957 private void dumpDumpFiles(PrintWriter pw) { 3958 synchronized (mLock) { 3959 pw.println("** SHORTCUT MANAGER FILES (dumpsys shortcut -n -f)"); 3960 mShortcutDumpFiles.dumpAll(pw); 3961 } 3962 } 3963 3964 // === Shell support === 3965 3966 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)3967 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 3968 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 3969 3970 enforceShell(); 3971 3972 final long token = injectClearCallingIdentity(); 3973 try { 3974 final int status = (new MyShellCommand()).exec(this, in, out, err, args, callback, 3975 resultReceiver); 3976 resultReceiver.send(status, null); 3977 } finally { 3978 injectRestoreCallingIdentity(token); 3979 } 3980 } 3981 3982 static class CommandException extends Exception { CommandException(String message)3983 public CommandException(String message) { 3984 super(message); 3985 } 3986 } 3987 3988 /** 3989 * Handle "adb shell cmd". 3990 */ 3991 private class MyShellCommand extends ShellCommand { 3992 3993 private int mUserId = UserHandle.USER_SYSTEM; 3994 parseOptionsLocked(boolean takeUser)3995 private void parseOptionsLocked(boolean takeUser) 3996 throws CommandException { 3997 String opt; 3998 while ((opt = getNextOption()) != null) { 3999 switch (opt) { 4000 case "--user": 4001 if (takeUser) { 4002 mUserId = UserHandle.parseUserArg(getNextArgRequired()); 4003 if (!isUserUnlockedL(mUserId)) { 4004 throw new CommandException( 4005 "User " + mUserId + " is not running or locked"); 4006 } 4007 break; 4008 } 4009 // fallthrough 4010 default: 4011 throw new CommandException("Unknown option: " + opt); 4012 } 4013 } 4014 } 4015 4016 @Override onCommand(String cmd)4017 public int onCommand(String cmd) { 4018 if (cmd == null) { 4019 return handleDefaultCommands(cmd); 4020 } 4021 final PrintWriter pw = getOutPrintWriter(); 4022 try { 4023 switch (cmd) { 4024 case "reset-throttling": 4025 handleResetThrottling(); 4026 break; 4027 case "reset-all-throttling": 4028 handleResetAllThrottling(); 4029 break; 4030 case "override-config": 4031 handleOverrideConfig(); 4032 break; 4033 case "reset-config": 4034 handleResetConfig(); 4035 break; 4036 case "clear-default-launcher": 4037 handleClearDefaultLauncher(); 4038 break; 4039 case "get-default-launcher": 4040 handleGetDefaultLauncher(); 4041 break; 4042 case "unload-user": 4043 handleUnloadUser(); 4044 break; 4045 case "clear-shortcuts": 4046 handleClearShortcuts(); 4047 break; 4048 case "verify-states": // hidden command to verify various internal states. 4049 handleVerifyStates(); 4050 break; 4051 default: 4052 return handleDefaultCommands(cmd); 4053 } 4054 } catch (CommandException e) { 4055 pw.println("Error: " + e.getMessage()); 4056 return 1; 4057 } 4058 pw.println("Success"); 4059 return 0; 4060 } 4061 4062 @Override onHelp()4063 public void onHelp() { 4064 final PrintWriter pw = getOutPrintWriter(); 4065 pw.println("Usage: cmd shortcut COMMAND [options ...]"); 4066 pw.println(); 4067 pw.println("cmd shortcut reset-throttling [--user USER_ID]"); 4068 pw.println(" Reset throttling for all packages and users"); 4069 pw.println(); 4070 pw.println("cmd shortcut reset-all-throttling"); 4071 pw.println(" Reset the throttling state for all users"); 4072 pw.println(); 4073 pw.println("cmd shortcut override-config CONFIG"); 4074 pw.println(" Override the configuration for testing (will last until reboot)"); 4075 pw.println(); 4076 pw.println("cmd shortcut reset-config"); 4077 pw.println(" Reset the configuration set with \"update-config\""); 4078 pw.println(); 4079 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]"); 4080 pw.println(" Clear the cached default launcher"); 4081 pw.println(); 4082 pw.println("cmd shortcut get-default-launcher [--user USER_ID]"); 4083 pw.println(" Show the default launcher"); 4084 pw.println(); 4085 pw.println("cmd shortcut unload-user [--user USER_ID]"); 4086 pw.println(" Unload a user from the memory"); 4087 pw.println(" (This should not affect any observable behavior)"); 4088 pw.println(); 4089 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE"); 4090 pw.println(" Remove all shortcuts from a package, including pinned shortcuts"); 4091 pw.println(); 4092 } 4093 handleResetThrottling()4094 private void handleResetThrottling() throws CommandException { 4095 synchronized (mLock) { 4096 parseOptionsLocked(/* takeUser =*/ true); 4097 4098 Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId); 4099 4100 resetThrottlingInner(mUserId); 4101 } 4102 } 4103 handleResetAllThrottling()4104 private void handleResetAllThrottling() { 4105 Slog.i(TAG, "cmd: handleResetAllThrottling"); 4106 4107 resetAllThrottlingInner(); 4108 } 4109 handleOverrideConfig()4110 private void handleOverrideConfig() throws CommandException { 4111 final String config = getNextArgRequired(); 4112 4113 Slog.i(TAG, "cmd: handleOverrideConfig: " + config); 4114 4115 synchronized (mLock) { 4116 if (!updateConfigurationLocked(config)) { 4117 throw new CommandException("override-config failed. See logcat for details."); 4118 } 4119 } 4120 } 4121 handleResetConfig()4122 private void handleResetConfig() { 4123 Slog.i(TAG, "cmd: handleResetConfig"); 4124 4125 synchronized (mLock) { 4126 loadConfigurationLocked(); 4127 } 4128 } 4129 clearLauncher()4130 private void clearLauncher() { 4131 synchronized (mLock) { 4132 getUserShortcutsLocked(mUserId).forceClearLauncher(); 4133 } 4134 } 4135 showLauncher()4136 private void showLauncher() { 4137 synchronized (mLock) { 4138 // This ensures to set the cached launcher. Package name doesn't matter. 4139 hasShortcutHostPermissionInner("-", mUserId); 4140 4141 getOutPrintWriter().println("Launcher: " 4142 + getUserShortcutsLocked(mUserId).getLastKnownLauncher()); 4143 } 4144 } 4145 handleClearDefaultLauncher()4146 private void handleClearDefaultLauncher() throws CommandException { 4147 synchronized (mLock) { 4148 parseOptionsLocked(/* takeUser =*/ true); 4149 4150 clearLauncher(); 4151 } 4152 } 4153 handleGetDefaultLauncher()4154 private void handleGetDefaultLauncher() throws CommandException { 4155 synchronized (mLock) { 4156 parseOptionsLocked(/* takeUser =*/ true); 4157 4158 clearLauncher(); 4159 showLauncher(); 4160 } 4161 } 4162 handleUnloadUser()4163 private void handleUnloadUser() throws CommandException { 4164 synchronized (mLock) { 4165 parseOptionsLocked(/* takeUser =*/ true); 4166 4167 Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId); 4168 4169 ShortcutService.this.handleStopUser(mUserId); 4170 } 4171 } 4172 handleClearShortcuts()4173 private void handleClearShortcuts() throws CommandException { 4174 synchronized (mLock) { 4175 parseOptionsLocked(/* takeUser =*/ true); 4176 final String packageName = getNextArgRequired(); 4177 4178 Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName); 4179 4180 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId, 4181 /* appStillExists = */ true); 4182 } 4183 } 4184 handleVerifyStates()4185 private void handleVerifyStates() throws CommandException { 4186 try { 4187 verifyStatesForce(); // This will throw when there's an issue. 4188 } catch (Throwable th) { 4189 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th)); 4190 } 4191 } 4192 } 4193 4194 // === Unit test support === 4195 4196 // Injection point. 4197 @VisibleForTesting injectCurrentTimeMillis()4198 long injectCurrentTimeMillis() { 4199 return System.currentTimeMillis(); 4200 } 4201 4202 @VisibleForTesting injectElapsedRealtime()4203 long injectElapsedRealtime() { 4204 return SystemClock.elapsedRealtime(); 4205 } 4206 4207 @VisibleForTesting injectUptimeMillis()4208 long injectUptimeMillis() { 4209 return SystemClock.uptimeMillis(); 4210 } 4211 4212 // Injection point. 4213 @VisibleForTesting injectBinderCallingUid()4214 int injectBinderCallingUid() { 4215 return getCallingUid(); 4216 } 4217 4218 @VisibleForTesting injectBinderCallingPid()4219 int injectBinderCallingPid() { 4220 return getCallingPid(); 4221 } 4222 getCallingUserId()4223 private int getCallingUserId() { 4224 return UserHandle.getUserId(injectBinderCallingUid()); 4225 } 4226 4227 // Injection point. 4228 @VisibleForTesting injectClearCallingIdentity()4229 long injectClearCallingIdentity() { 4230 return Binder.clearCallingIdentity(); 4231 } 4232 4233 // Injection point. 4234 @VisibleForTesting injectRestoreCallingIdentity(long token)4235 void injectRestoreCallingIdentity(long token) { 4236 Binder.restoreCallingIdentity(token); 4237 } 4238 4239 // Injection point. injectBuildFingerprint()4240 String injectBuildFingerprint() { 4241 return Build.FINGERPRINT; 4242 } 4243 wtf(String message)4244 final void wtf(String message) { 4245 wtf(message, /* exception= */ null); 4246 } 4247 4248 // Injection point. wtf(String message, Throwable e)4249 void wtf(String message, Throwable e) { 4250 if (e == null) { 4251 e = new RuntimeException("Stacktrace"); 4252 } 4253 synchronized (mLock) { 4254 mWtfCount++; 4255 mLastWtfStacktrace = new Exception("Last failure was logged here:"); 4256 } 4257 Slog.wtf(TAG, message, e); 4258 } 4259 4260 @VisibleForTesting injectSystemDataPath()4261 File injectSystemDataPath() { 4262 return Environment.getDataSystemDirectory(); 4263 } 4264 4265 @VisibleForTesting injectUserDataPath(@serIdInt int userId)4266 File injectUserDataPath(@UserIdInt int userId) { 4267 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); 4268 } 4269 getDumpPath()4270 public File getDumpPath() { 4271 return new File(injectUserDataPath(UserHandle.USER_SYSTEM), DIRECTORY_DUMP); 4272 } 4273 4274 @VisibleForTesting injectIsLowRamDevice()4275 boolean injectIsLowRamDevice() { 4276 return ActivityManager.isLowRamDeviceStatic(); 4277 } 4278 4279 @VisibleForTesting injectRegisterUidObserver(IUidObserver observer, int which)4280 void injectRegisterUidObserver(IUidObserver observer, int which) { 4281 try { 4282 ActivityManager.getService().registerUidObserver(observer, which, 4283 ActivityManager.PROCESS_STATE_UNKNOWN, null); 4284 } catch (RemoteException shouldntHappen) { 4285 } 4286 } 4287 getUserBitmapFilePath(@serIdInt int userId)4288 File getUserBitmapFilePath(@UserIdInt int userId) { 4289 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); 4290 } 4291 4292 @VisibleForTesting getShortcutsForTest()4293 SparseArray<ShortcutUser> getShortcutsForTest() { 4294 return mUsers; 4295 } 4296 4297 @VisibleForTesting getMaxShortcutsForTest()4298 int getMaxShortcutsForTest() { 4299 return mMaxShortcuts; 4300 } 4301 4302 @VisibleForTesting getMaxUpdatesPerIntervalForTest()4303 int getMaxUpdatesPerIntervalForTest() { 4304 return mMaxUpdatesPerInterval; 4305 } 4306 4307 @VisibleForTesting getResetIntervalForTest()4308 long getResetIntervalForTest() { 4309 return mResetInterval; 4310 } 4311 4312 @VisibleForTesting getMaxIconDimensionForTest()4313 int getMaxIconDimensionForTest() { 4314 return mMaxIconDimension; 4315 } 4316 4317 @VisibleForTesting getIconPersistFormatForTest()4318 CompressFormat getIconPersistFormatForTest() { 4319 return mIconPersistFormat; 4320 } 4321 4322 @VisibleForTesting getIconPersistQualityForTest()4323 int getIconPersistQualityForTest() { 4324 return mIconPersistQuality; 4325 } 4326 4327 @VisibleForTesting getPackageShortcutForTest(String packageName, int userId)4328 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) { 4329 synchronized (mLock) { 4330 final ShortcutUser user = mUsers.get(userId); 4331 if (user == null) return null; 4332 4333 return user.getAllPackagesForTest().get(packageName); 4334 } 4335 } 4336 4337 @VisibleForTesting getPackageShortcutForTest(String packageName, String shortcutId, int userId)4338 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) { 4339 synchronized (mLock) { 4340 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId); 4341 if (pkg == null) return null; 4342 4343 return pkg.findShortcutById(shortcutId); 4344 } 4345 } 4346 4347 @VisibleForTesting getLauncherShortcutForTest(String packageName, int userId)4348 ShortcutLauncher getLauncherShortcutForTest(String packageName, int userId) { 4349 synchronized (mLock) { 4350 final ShortcutUser user = mUsers.get(userId); 4351 if (user == null) return null; 4352 4353 return user.getAllLaunchersForTest().get(PackageWithUser.of(userId, packageName)); 4354 } 4355 } 4356 4357 @VisibleForTesting getShortcutRequestPinProcessorForTest()4358 ShortcutRequestPinProcessor getShortcutRequestPinProcessorForTest() { 4359 return mShortcutRequestPinProcessor; 4360 } 4361 4362 /** 4363 * Control whether {@link #verifyStates} should be performed. We always perform it during unit 4364 * tests. 4365 */ 4366 @VisibleForTesting injectShouldPerformVerification()4367 boolean injectShouldPerformVerification() { 4368 return DEBUG; 4369 } 4370 4371 /** 4372 * Check various internal states and throws if there's any inconsistency. 4373 * This is normally only enabled during unit tests. 4374 */ verifyStates()4375 final void verifyStates() { 4376 if (injectShouldPerformVerification()) { 4377 verifyStatesInner(); 4378 } 4379 } 4380 verifyStatesForce()4381 private final void verifyStatesForce() { 4382 verifyStatesInner(); 4383 } 4384 verifyStatesInner()4385 private void verifyStatesInner() { 4386 synchronized (mLock) { 4387 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); 4388 } 4389 } 4390 4391 @VisibleForTesting waitForBitmapSavesForTest()4392 void waitForBitmapSavesForTest() { 4393 synchronized (mLock) { 4394 mShortcutBitmapSaver.waitForAllSavesLocked(); 4395 } 4396 } 4397 } 4398