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