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