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