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