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