1 /* 2 * Copyright (C) 2014 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 17 package android.content.pm; 18 19 import static android.Manifest.permission; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SdkConstant; 27 import android.annotation.SdkConstant.SdkConstantType; 28 import android.annotation.SystemApi; 29 import android.annotation.SystemService; 30 import android.annotation.TestApi; 31 import android.app.PendingIntent; 32 import android.appwidget.AppWidgetManager; 33 import android.appwidget.AppWidgetProviderInfo; 34 import android.compat.annotation.UnsupportedAppUsage; 35 import android.content.ActivityNotFoundException; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.IntentSender; 40 import android.content.LocusId; 41 import android.content.pm.PackageInstaller.SessionCallback; 42 import android.content.pm.PackageInstaller.SessionCallbackDelegate; 43 import android.content.pm.PackageInstaller.SessionInfo; 44 import android.content.pm.PackageManager.ApplicationInfoFlags; 45 import android.content.pm.PackageManager.NameNotFoundException; 46 import android.content.res.Resources; 47 import android.graphics.Bitmap; 48 import android.graphics.BitmapFactory; 49 import android.graphics.Rect; 50 import android.graphics.drawable.AdaptiveIconDrawable; 51 import android.graphics.drawable.BitmapDrawable; 52 import android.graphics.drawable.Drawable; 53 import android.graphics.drawable.Icon; 54 import android.net.Uri; 55 import android.os.Build; 56 import android.os.Bundle; 57 import android.os.Handler; 58 import android.os.Looper; 59 import android.os.Message; 60 import android.os.Parcel; 61 import android.os.ParcelFileDescriptor; 62 import android.os.Parcelable; 63 import android.os.RemoteException; 64 import android.os.ServiceManager; 65 import android.os.UserHandle; 66 import android.os.UserManager; 67 import android.util.DisplayMetrics; 68 import android.util.Log; 69 import android.util.Pair; 70 71 import com.android.internal.annotations.VisibleForTesting; 72 import com.android.internal.util.function.pooled.PooledLambda; 73 74 import java.io.FileNotFoundException; 75 import java.io.IOException; 76 import java.lang.annotation.Retention; 77 import java.lang.annotation.RetentionPolicy; 78 import java.lang.ref.WeakReference; 79 import java.util.ArrayList; 80 import java.util.Arrays; 81 import java.util.Collections; 82 import java.util.HashMap; 83 import java.util.Iterator; 84 import java.util.List; 85 import java.util.Map; 86 import java.util.Objects; 87 import java.util.concurrent.Executor; 88 89 /** 90 * Class for retrieving a list of launchable activities for the current user and any associated 91 * managed profiles that are visible to the current user, which can be retrieved with 92 * {@link #getProfiles}. This is mainly for use by launchers. 93 * 94 * Apps can be queried for each user profile. 95 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register 96 * for package changes here. 97 * <p> 98 * To watch for managed profiles being added or removed, register for the following broadcasts: 99 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}. 100 * <p> 101 * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the 102 * main profile. Apps can only access profiles returned by {@link #getProfiles()}. 103 */ 104 @SystemService(Context.LAUNCHER_APPS_SERVICE) 105 public class LauncherApps { 106 107 static final String TAG = "LauncherApps"; 108 static final boolean DEBUG = false; 109 110 /** 111 * Activity Action: For the default launcher to show the confirmation dialog to create 112 * a pinned shortcut. 113 * 114 * <p>See the {@link ShortcutManager} javadoc for details. 115 * 116 * <p> 117 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 118 * and call {@link PinItemRequest#accept(Bundle)} 119 * if the user accepts. If the user doesn't accept, no further action is required. 120 * 121 * @see #EXTRA_PIN_ITEM_REQUEST 122 */ 123 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 124 public static final String ACTION_CONFIRM_PIN_SHORTCUT = 125 "android.content.pm.action.CONFIRM_PIN_SHORTCUT"; 126 127 /** 128 * Activity Action: For the default launcher to show the confirmation dialog to create 129 * a pinned app widget. 130 * 131 * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for 132 * details. 133 * 134 * <p> 135 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 136 * and call {@link PinItemRequest#accept(Bundle)} 137 * if the user accepts. If the user doesn't accept, no further action is required. 138 * 139 * @see #EXTRA_PIN_ITEM_REQUEST 140 */ 141 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 142 public static final String ACTION_CONFIRM_PIN_APPWIDGET = 143 "android.content.pm.action.CONFIRM_PIN_APPWIDGET"; 144 145 /** 146 * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} & {@link #ACTION_CONFIRM_PIN_APPWIDGET} 147 * containing a {@link PinItemRequest} of appropriate type asked to pin. 148 * 149 * <p>A helper function {@link #getPinItemRequest(Intent)} can be used 150 * instead of using this constant directly. 151 * 152 * @see #ACTION_CONFIRM_PIN_SHORTCUT 153 * @see #ACTION_CONFIRM_PIN_APPWIDGET 154 */ 155 public static final String EXTRA_PIN_ITEM_REQUEST = 156 "android.content.pm.extra.PIN_ITEM_REQUEST"; 157 158 /** 159 * Cache shortcuts which are used in notifications. 160 * @hide 161 */ 162 public static final int FLAG_CACHE_NOTIFICATION_SHORTCUTS = 0; 163 164 /** 165 * Cache shortcuts which are used in bubbles. 166 * @hide 167 */ 168 public static final int FLAG_CACHE_BUBBLE_SHORTCUTS = 1; 169 170 /** @hide */ 171 @IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = { 172 FLAG_CACHE_NOTIFICATION_SHORTCUTS, 173 FLAG_CACHE_BUBBLE_SHORTCUTS, 174 }) 175 @Retention(RetentionPolicy.SOURCE) 176 public @interface ShortcutCacheFlags {} 177 178 private final Context mContext; 179 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 180 private final ILauncherApps mService; 181 @UnsupportedAppUsage 182 private final PackageManager mPm; 183 private final UserManager mUserManager; 184 185 private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>(); 186 private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>(); 187 188 private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>> 189 mShortcutChangeCallbacks = new HashMap<>(); 190 191 /** 192 * Callbacks for package changes to this and related managed profiles. 193 */ 194 public static abstract class Callback { 195 /** 196 * Indicates that a package was removed from the specified profile. 197 * 198 * If a package is removed while being updated onPackageChanged will be 199 * called instead. 200 * 201 * @param packageName The name of the package that was removed. 202 * @param user The UserHandle of the profile that generated the change. 203 */ onPackageRemoved(String packageName, UserHandle user)204 abstract public void onPackageRemoved(String packageName, UserHandle user); 205 206 /** 207 * Indicates that a package was added to the specified profile. 208 * 209 * If a package is added while being updated then onPackageChanged will be 210 * called instead. 211 * 212 * @param packageName The name of the package that was added. 213 * @param user The UserHandle of the profile that generated the change. 214 */ onPackageAdded(String packageName, UserHandle user)215 abstract public void onPackageAdded(String packageName, UserHandle user); 216 217 /** 218 * Indicates that a package was modified in the specified profile. 219 * This can happen, for example, when the package is updated or when 220 * one or more components are enabled or disabled. 221 * 222 * @param packageName The name of the package that has changed. 223 * @param user The UserHandle of the profile that generated the change. 224 */ onPackageChanged(String packageName, UserHandle user)225 abstract public void onPackageChanged(String packageName, UserHandle user); 226 227 /** 228 * Indicates that one or more packages have become available. For 229 * example, this can happen when a removable storage card has 230 * reappeared. 231 * 232 * @param packageNames The names of the packages that have become 233 * available. 234 * @param user The UserHandle of the profile that generated the change. 235 * @param replacing Indicates whether these packages are replacing 236 * existing ones. 237 */ onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)238 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user, 239 boolean replacing); 240 241 /** 242 * Indicates that one or more packages have become unavailable. For 243 * example, this can happen when a removable storage card has been 244 * removed. 245 * 246 * @param packageNames The names of the packages that have become 247 * unavailable. 248 * @param user The UserHandle of the profile that generated the change. 249 * @param replacing Indicates whether the packages are about to be 250 * replaced with new versions. 251 */ onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)252 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user, 253 boolean replacing); 254 255 /** 256 * Indicates that one or more packages have been suspended. For 257 * example, this can happen when a Device Administrator suspends 258 * an applicaton. 259 * 260 * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher, 261 * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will 262 * not receive this callback. 263 * 264 * @param packageNames The names of the packages that have just been 265 * suspended. 266 * @param user The UserHandle of the profile that generated the change. 267 */ onPackagesSuspended(String[] packageNames, UserHandle user)268 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 269 } 270 271 /** 272 * Indicates that one or more packages have been suspended. A device administrator or an app 273 * with {@code android.permission.SUSPEND_APPS} can do this. 274 * 275 * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can 276 * optionally provide a {@link Bundle} of extra information that it deems helpful for the 277 * launcher to handle the suspended state of these packages. The contents of this 278 * {@link Bundle} are supposed to be a contract between the suspending app and the launcher. 279 * 280 * @param packageNames The names of the packages that have just been suspended. 281 * @param user the user for which the given packages were suspended. 282 * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the 283 * system, {@code null} otherwise. 284 * @see PackageManager#isPackageSuspended() 285 * @see #getSuspendedPackageLauncherExtras(String, UserHandle) 286 * @deprecated {@code launcherExtras} should be obtained by using 287 * {@link #getSuspendedPackageLauncherExtras(String, UserHandle)}. For all other cases, 288 * {@link #onPackagesSuspended(String[], UserHandle)} should be used. 289 */ 290 @Deprecated onPackagesSuspended(String[] packageNames, UserHandle user, @Nullable Bundle launcherExtras)291 public void onPackagesSuspended(String[] packageNames, UserHandle user, 292 @Nullable Bundle launcherExtras) { 293 onPackagesSuspended(packageNames, user); 294 } 295 296 /** 297 * Indicates that one or more packages have been unsuspended. For 298 * example, this can happen when a Device Administrator unsuspends 299 * an applicaton. 300 * 301 * @param packageNames The names of the packages that have just been 302 * unsuspended. 303 * @param user The UserHandle of the profile that generated the change. 304 */ onPackagesUnsuspended(String[] packageNames, UserHandle user)305 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 306 } 307 308 /** 309 * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest) 310 * have been added, updated or removed. 311 * 312 * <p>Only the applications that are allowed to access the shortcut information, 313 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 314 * 315 * @param packageName The name of the package that has the shortcuts. 316 * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned). 317 * Only "key" information will be provided, as defined in 318 * {@link ShortcutInfo#hasKeyFieldsOnly()}. 319 * @param user The UserHandle of the profile that generated the change. 320 * 321 * @see ShortcutManager 322 */ onShortcutsChanged(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)323 public void onShortcutsChanged(@NonNull String packageName, 324 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 325 } 326 } 327 328 /** 329 * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}. 330 */ 331 public static class ShortcutQuery { 332 /** 333 * Include dynamic shortcuts in the result. 334 */ 335 public static final int FLAG_MATCH_DYNAMIC = 1 << 0; 336 337 /** @hide kept for unit tests */ 338 @Deprecated 339 public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC; 340 341 /** 342 * Include pinned shortcuts in the result. 343 * 344 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 345 * user owns on the launcher (or by other launchers, in case the user has multiple), use 346 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 347 * 348 * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other 349 * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this 350 * flag to get own pinned shortcuts. 351 */ 352 public static final int FLAG_MATCH_PINNED = 1 << 1; 353 354 /** @hide kept for unit tests */ 355 @Deprecated 356 public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED; 357 358 /** 359 * Include manifest shortcuts in the result. 360 */ 361 public static final int FLAG_MATCH_MANIFEST = 1 << 3; 362 363 /** 364 * Include cached shortcuts in the result. 365 */ 366 public static final int FLAG_MATCH_CACHED = 1 << 4; 367 368 /** @hide kept for unit tests */ 369 @Deprecated 370 public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; 371 372 /** 373 * Include all pinned shortcuts by any launchers, not just by the caller, 374 * in the result. 375 * 376 * <p>The caller must be the selected assistant app to use this flag, or have the system 377 * {@code ACCESS_SHORTCUTS} permission. 378 * 379 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 380 * user owns on the launcher (or by other launchers, in case the user has multiple), use 381 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 382 * 383 * <p>If you're a regular launcher app (or any app that's not the selected assistant app) 384 * then this flag will be ignored. 385 */ 386 public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10; 387 388 /** 389 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED 390 * @hide 391 */ 392 public static final int FLAG_MATCH_ALL_KINDS = 393 FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED; 394 395 /** 396 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED 397 * @hide 398 */ 399 public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED = 400 FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; 401 402 /** @hide kept for unit tests */ 403 @Deprecated 404 public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS; 405 406 /** 407 * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to 408 * see which fields fields "key". 409 * This allows quicker access to shortcut information in order to 410 * determine whether the caller's in-memory cache needs to be updated. 411 * 412 * <p>Typically, launcher applications cache all or most shortcut information 413 * in memory in order to show shortcuts without a delay. 414 * 415 * When a given launcher application wants to update its cache, such as when its process 416 * restarts, it can fetch shortcut information with this flag. 417 * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each 418 * shortcut, fetching a shortcut's non-key information only if that shortcut has been 419 * updated. 420 * 421 * @see ShortcutManager 422 */ 423 public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; 424 425 /** @hide */ 426 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 427 FLAG_MATCH_DYNAMIC, 428 FLAG_MATCH_PINNED, 429 FLAG_MATCH_MANIFEST, 430 FLAG_MATCH_CACHED, 431 FLAG_GET_KEY_FIELDS_ONLY, 432 }) 433 @Retention(RetentionPolicy.SOURCE) 434 public @interface QueryFlags {} 435 436 long mChangedSince; 437 438 @Nullable 439 String mPackage; 440 441 @Nullable 442 List<String> mShortcutIds; 443 444 @Nullable 445 List<LocusId> mLocusIds; 446 447 @Nullable 448 ComponentName mActivity; 449 450 @QueryFlags 451 int mQueryFlags; 452 ShortcutQuery()453 public ShortcutQuery() { 454 } 455 456 /** 457 * If non-zero, returns only shortcuts that have been added or updated 458 * since the given timestamp, expressed in milliseconds since the Epoch—see 459 * {@link System#currentTimeMillis()}. 460 */ setChangedSince(long changedSince)461 public ShortcutQuery setChangedSince(long changedSince) { 462 mChangedSince = changedSince; 463 return this; 464 } 465 466 /** 467 * If non-null, returns only shortcuts from the package. 468 */ setPackage(@ullable String packageName)469 public ShortcutQuery setPackage(@Nullable String packageName) { 470 mPackage = packageName; 471 return this; 472 } 473 474 /** 475 * If non-null, return only the specified shortcuts by ID. When setting this field, 476 * a package name must also be set with {@link #setPackage}. 477 */ setShortcutIds(@ullable List<String> shortcutIds)478 public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) { 479 mShortcutIds = shortcutIds; 480 return this; 481 } 482 483 /** 484 * If non-null, return only the specified shortcuts by locus ID. When setting this field, 485 * a package name must also be set with {@link #setPackage}. 486 */ 487 @NonNull setLocusIds(@ullable List<LocusId> locusIds)488 public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) { 489 mLocusIds = locusIds; 490 return this; 491 } 492 493 /** 494 * If non-null, returns only shortcuts associated with the activity; i.e. 495 * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal 496 * to {@code activity}. 497 */ setActivity(@ullable ComponentName activity)498 public ShortcutQuery setActivity(@Nullable ComponentName activity) { 499 mActivity = activity; 500 return this; 501 } 502 503 /** 504 * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, 505 * no shortcuts will be returned. 506 * 507 * <ul> 508 * <li>{@link #FLAG_MATCH_DYNAMIC} 509 * <li>{@link #FLAG_MATCH_PINNED} 510 * <li>{@link #FLAG_MATCH_MANIFEST} 511 * <li>{@link #FLAG_MATCH_CACHED} 512 * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY} 513 * </ul> 514 */ setQueryFlags(@ueryFlags int queryFlags)515 public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) { 516 mQueryFlags = queryFlags; 517 return this; 518 } 519 } 520 521 /** 522 * Callbacks for shortcut changes to this and related managed profiles. 523 * 524 * @hide 525 */ 526 public interface ShortcutChangeCallback { 527 /** 528 * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to 529 * register this callback, have been added or updated. 530 * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, 531 * Executor) 532 * 533 * <p>Only the applications that are allowed to access the shortcut information, 534 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 535 * 536 * @param packageName The name of the package that has the shortcuts. 537 * @param shortcuts Shortcuts from the package that have updated or added. Only "key" 538 * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. 539 * @param user The UserHandle of the profile that generated the change. 540 * 541 * @see ShortcutManager 542 */ onShortcutsAddedOrUpdated(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)543 default void onShortcutsAddedOrUpdated(@NonNull String packageName, 544 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {} 545 546 /** 547 * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to 548 * register this callback, have been removed. 549 * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, 550 * Executor) 551 * 552 * <p>Only the applications that are allowed to access the shortcut information, 553 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 554 * 555 * @param packageName The name of the package that has the shortcuts. 556 * @param shortcuts Shortcuts from the package that have been removed. Only "key" 557 * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. 558 * @param user The UserHandle of the profile that generated the change. 559 * 560 * @see ShortcutManager 561 */ onShortcutsRemoved(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)562 default void onShortcutsRemoved(@NonNull String packageName, 563 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {} 564 } 565 566 /** 567 * Callback proxy class for {@link ShortcutChangeCallback} 568 * 569 * @hide 570 */ 571 private static class ShortcutChangeCallbackProxy extends 572 android.content.pm.IShortcutChangeCallback.Stub { 573 private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences; 574 ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback)575 ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) { 576 mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback)); 577 } 578 579 @Override onShortcutsAddedOrUpdated(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)580 public void onShortcutsAddedOrUpdated(@NonNull String packageName, 581 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 582 Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get(); 583 if (remoteReferences == null) { 584 // Binder is dead. 585 return; 586 } 587 588 final Executor executor = remoteReferences.first; 589 final ShortcutChangeCallback callback = remoteReferences.second; 590 executor.execute( 591 PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsAddedOrUpdated, 592 callback, packageName, shortcuts, user).recycleOnUse()); 593 } 594 595 @Override onShortcutsRemoved(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)596 public void onShortcutsRemoved(@NonNull String packageName, 597 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 598 Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get(); 599 if (remoteReferences == null) { 600 // Binder is dead. 601 return; 602 } 603 604 final Executor executor = remoteReferences.first; 605 final ShortcutChangeCallback callback = remoteReferences.second; 606 executor.execute( 607 PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsRemoved, 608 callback, packageName, shortcuts, user).recycleOnUse()); 609 } 610 } 611 612 /** @hide */ LauncherApps(Context context, ILauncherApps service)613 public LauncherApps(Context context, ILauncherApps service) { 614 mContext = context; 615 mService = service; 616 mPm = context.getPackageManager(); 617 mUserManager = context.getSystemService(UserManager.class); 618 } 619 620 /** @hide */ 621 @TestApi LauncherApps(Context context)622 public LauncherApps(Context context) { 623 this(context, ILauncherApps.Stub.asInterface( 624 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE))); 625 } 626 627 /** 628 * Show an error log on logcat, when the calling user is a managed profile, the target 629 * user is different from the calling user, and it is not called from a package that has the 630 * {@link permission.INTERACT_ACROSS_USERS_FULL} permission, in order to help 631 * developers to detect it. 632 */ logErrorForInvalidProfileAccess(@onNull UserHandle target)633 private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) { 634 if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile() 635 && mContext.checkSelfPermission(permission.INTERACT_ACROSS_USERS_FULL) 636 != PackageManager.PERMISSION_GRANTED) { 637 Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); 638 } 639 } 640 641 /** 642 * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs. 643 * 644 * <p>If the caller is running on a managed profile, it'll return only the current profile. 645 * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. 646 */ getProfiles()647 public List<UserHandle> getProfiles() { 648 if (mUserManager.isManagedProfile()) { 649 // If it's a managed profile, only return the current profile. 650 final List result = new ArrayList(1); 651 result.add(android.os.Process.myUserHandle()); 652 return result; 653 } else { 654 return mUserManager.getUserProfiles(); 655 } 656 } 657 658 /** 659 * Retrieves a list of activities that specify {@link Intent#ACTION_MAIN} and 660 * {@link Intent#CATEGORY_LAUNCHER}, across all apps, for a specified user. If an app doesn't 661 * have any activities that specify <code>ACTION_MAIN</code> or <code>CATEGORY_LAUNCHER</code>, 662 * the system adds a synthesized activity to the list. This synthesized activity represents the 663 * app's details page within system settings. 664 * 665 * <p class="note"><b>Note: </b>It's possible for system apps, such as app stores, to prevent 666 * the system from adding synthesized activities to the returned list.</p> 667 * 668 * <p>As of <a href="/reference/android/os/Build.VERSION_CODES.html#Q">Android Q</a>, at least 669 * one of the app's activities or synthesized activities appears in the returned list unless the 670 * app satisfies at least one of the following conditions:</p> 671 * <ul> 672 * <li>The app is a system app.</li> 673 * <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>. 674 * </li> 675 * <li>The app doesn't have a <em>launcher activity</em> that is enabled by default. A launcher 676 * activity has an intent containing the <code>ACTION_MAIN</code> action and the 677 * <code>CATEGORY_LAUNCHER</code> category.</li> 678 * </ul> 679 * 680 * <p>Additionally, the system hides synthesized activities for some or all apps in the 681 * following enterprise-related cases:</p> 682 * <ul> 683 * <li>If the device is a 684 * <a href="https://developers.google.com/android/work/overview#company-owned-devices-for-knowledge-workers">fully 685 * managed device</a>, no synthesized activities for any app appear in the returned list.</li> 686 * <li>If the current user has a 687 * <a href="https://developers.google.com/android/work/overview#employee-owned-devices-byod">work 688 * profile</a>, no synthesized activities for the user's work apps appear in the returned 689 * list.</li> 690 * </ul> 691 * 692 * @param packageName The specific package to query. If null, it checks all installed packages 693 * in the profile. 694 * @param user The UserHandle of the profile. 695 * @return List of launchable activities. Can be an empty list but will not be null. 696 */ getActivityList(String packageName, UserHandle user)697 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { 698 logErrorForInvalidProfileAccess(user); 699 try { 700 return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(), 701 packageName, user), user); 702 } catch (RemoteException re) { 703 throw re.rethrowFromSystemServer(); 704 } 705 } 706 707 /** 708 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it 709 * returns null. 710 * 711 * @param intent The intent to find a match for. 712 * @param user The profile to look in for a match. 713 * @return An activity info object if there is a match. 714 */ resolveActivity(Intent intent, UserHandle user)715 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) { 716 logErrorForInvalidProfileAccess(user); 717 try { 718 ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(), 719 intent.getComponent(), user); 720 if (ai != null) { 721 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user); 722 return info; 723 } 724 } catch (RemoteException re) { 725 throw re.rethrowFromSystemServer(); 726 } 727 return null; 728 } 729 730 /** 731 * Starts a Main activity in the specified profile. 732 * 733 * @param component The ComponentName of the activity to launch 734 * @param user The UserHandle of the profile 735 * @param sourceBounds The Rect containing the source bounds of the clicked icon 736 * @param opts Options to pass to startActivity 737 */ startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)738 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, 739 Bundle opts) { 740 logErrorForInvalidProfileAccess(user); 741 if (DEBUG) { 742 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier()); 743 } 744 try { 745 mService.startActivityAsUser(mContext.getIApplicationThread(), 746 mContext.getPackageName(), mContext.getAttributionTag(), 747 component, sourceBounds, opts, user); 748 } catch (RemoteException re) { 749 throw re.rethrowFromSystemServer(); 750 } 751 } 752 753 /** 754 * Starts an activity to show the details of the specified session. 755 * 756 * @param sessionInfo The SessionInfo of the session 757 * @param sourceBounds The Rect containing the source bounds of the clicked icon 758 * @param opts Options to pass to startActivity 759 */ startPackageInstallerSessionDetailsActivity(@onNull SessionInfo sessionInfo, @Nullable Rect sourceBounds, @Nullable Bundle opts)760 public void startPackageInstallerSessionDetailsActivity(@NonNull SessionInfo sessionInfo, 761 @Nullable Rect sourceBounds, @Nullable Bundle opts) { 762 try { 763 mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(), 764 mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo, 765 sourceBounds, opts, sessionInfo.getUser()); 766 } catch (RemoteException re) { 767 throw re.rethrowFromSystemServer(); 768 } 769 } 770 771 /** 772 * Starts the settings activity to show the application details for a 773 * package in the specified profile. 774 * 775 * @param component The ComponentName of the package to launch settings for. 776 * @param user The UserHandle of the profile 777 * @param sourceBounds The Rect containing the source bounds of the clicked icon 778 * @param opts Options to pass to startActivity 779 */ startAppDetailsActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)780 public void startAppDetailsActivity(ComponentName component, UserHandle user, 781 Rect sourceBounds, Bundle opts) { 782 logErrorForInvalidProfileAccess(user); 783 try { 784 mService.showAppDetailsAsUser(mContext.getIApplicationThread(), 785 mContext.getPackageName(), mContext.getAttributionTag(), 786 component, sourceBounds, opts, user); 787 } catch (RemoteException re) { 788 throw re.rethrowFromSystemServer(); 789 } 790 } 791 792 /** 793 * Retrieves a list of config activities for creating {@link ShortcutInfo}. 794 * 795 * @param packageName The specific package to query. If null, it checks all installed packages 796 * in the profile. 797 * @param user The UserHandle of the profile. 798 * @return List of config activities. Can be an empty list but will not be null. 799 * 800 * @see Intent#ACTION_CREATE_SHORTCUT 801 * @see #getShortcutConfigActivityIntent(LauncherActivityInfo) 802 */ getShortcutConfigActivityList(@ullable String packageName, @NonNull UserHandle user)803 public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName, 804 @NonNull UserHandle user) { 805 logErrorForInvalidProfileAccess(user); 806 try { 807 return convertToActivityList(mService.getShortcutConfigActivities( 808 mContext.getPackageName(), packageName, user), 809 user); 810 } catch (RemoteException re) { 811 throw re.rethrowFromSystemServer(); 812 } 813 } 814 convertToActivityList( @ullable ParceledListSlice<ResolveInfo> activities, UserHandle user)815 private List<LauncherActivityInfo> convertToActivityList( 816 @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) { 817 if (activities == null) { 818 return Collections.EMPTY_LIST; 819 } 820 ArrayList<LauncherActivityInfo> lais = new ArrayList<>(); 821 for (ResolveInfo ri : activities.getList()) { 822 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user); 823 if (DEBUG) { 824 Log.v(TAG, "Returning activity for profile " + user + " : " 825 + lai.getComponentName()); 826 } 827 lais.add(lai); 828 } 829 return lais; 830 } 831 832 /** 833 * Returns an intent sender which can be used to start the configure activity for creating 834 * custom shortcuts. Use this method if the provider is in another profile as you are not 835 * allowed to start an activity in another profile. 836 * 837 * <p>The caller should receive {@link PinItemRequest} in onActivityResult on 838 * {@link android.app.Activity#RESULT_OK}. 839 * 840 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 841 * #hasShortcutHostPermission()}. 842 * 843 * @param info a configuration activity returned by {@link #getShortcutConfigActivityList} 844 * 845 * @throws IllegalStateException when the user is locked or not running. 846 * @throws SecurityException if {@link #hasShortcutHostPermission()} is false. 847 * 848 * @see #getPinItemRequest(Intent) 849 * @see Intent#ACTION_CREATE_SHORTCUT 850 * @see android.app.Activity#startIntentSenderForResult 851 */ 852 @Nullable getShortcutConfigActivityIntent(@onNull LauncherActivityInfo info)853 public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) { 854 try { 855 return mService.getShortcutConfigActivityIntent( 856 mContext.getPackageName(), info.getComponentName(), info.getUser()); 857 } catch (RemoteException re) { 858 throw re.rethrowFromSystemServer(); 859 } 860 } 861 862 /** 863 * Checks if the package is installed and enabled for a profile. 864 * 865 * @param packageName The package to check. 866 * @param user The UserHandle of the profile. 867 * 868 * @return true if the package exists and is enabled. 869 */ isPackageEnabled(String packageName, UserHandle user)870 public boolean isPackageEnabled(String packageName, UserHandle user) { 871 logErrorForInvalidProfileAccess(user); 872 try { 873 return mService.isPackageEnabled(mContext.getPackageName(), packageName, user); 874 } catch (RemoteException re) { 875 throw re.rethrowFromSystemServer(); 876 } 877 } 878 879 /** 880 * Gets the launcher extras supplied to the system when the given package was suspended via 881 * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, 882 * PersistableBundle, String)}. 883 * 884 * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending 885 * app and the launcher. 886 * 887 * <p>Note: This just returns whatever extras were provided to the system, <em>which might 888 * even be {@code null}.</em> 889 * 890 * @param packageName The package for which to fetch the launcher extras. 891 * @param user The {@link UserHandle} of the profile. 892 * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently 893 * suspended. 894 * 895 * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle) 896 * @see PackageManager#isPackageSuspended() 897 */ getSuspendedPackageLauncherExtras(String packageName, UserHandle user)898 public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) { 899 logErrorForInvalidProfileAccess(user); 900 try { 901 return mService.getSuspendedPackageLauncherExtras(packageName, user); 902 } catch (RemoteException re) { 903 throw re.rethrowFromSystemServer(); 904 } 905 } 906 907 /** 908 * Returns whether a package should be hidden from suggestions to the user. Currently, this 909 * could be done because the package was marked as distracting to the user via 910 * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. 911 * 912 * @param packageName The package for which to check. 913 * @param user the {@link UserHandle} of the profile. 914 * @return 915 */ shouldHideFromSuggestions(@onNull String packageName, @NonNull UserHandle user)916 public boolean shouldHideFromSuggestions(@NonNull String packageName, 917 @NonNull UserHandle user) { 918 Objects.requireNonNull(packageName, "packageName"); 919 Objects.requireNonNull(user, "user"); 920 try { 921 return mService.shouldHideFromSuggestions(packageName, user); 922 } catch (RemoteException re) { 923 throw re.rethrowFromSystemServer(); 924 } 925 } 926 927 /** 928 * Returns {@link ApplicationInfo} about an application installed for a specific user profile. 929 * 930 * @param packageName The package name of the application 931 * @param flags Additional option flags {@link PackageManager#getApplicationInfo} 932 * @param user The UserHandle of the profile. 933 * 934 * @return {@link ApplicationInfo} containing information about the package. Returns 935 * {@code null} if the package isn't installed for the given profile, or the profile 936 * isn't enabled. 937 */ getApplicationInfo(@onNull String packageName, @ApplicationInfoFlags int flags, @NonNull UserHandle user)938 public ApplicationInfo getApplicationInfo(@NonNull String packageName, 939 @ApplicationInfoFlags int flags, @NonNull UserHandle user) 940 throws PackageManager.NameNotFoundException { 941 Objects.requireNonNull(packageName, "packageName"); 942 Objects.requireNonNull(user, "user"); 943 logErrorForInvalidProfileAccess(user); 944 try { 945 final ApplicationInfo ai = mService 946 .getApplicationInfo(mContext.getPackageName(), packageName, flags, user); 947 if (ai == null) { 948 throw new NameNotFoundException("Package " + packageName + " not found for user " 949 + user.getIdentifier()); 950 } 951 return ai; 952 } catch (RemoteException re) { 953 throw re.rethrowFromSystemServer(); 954 } 955 } 956 957 /** 958 * Returns an object describing the app usage limit for the given package. 959 * If there are multiple limits that apply to the package, the one with the smallest 960 * time remaining will be returned. 961 * 962 * @param packageName name of the package whose app usage limit will be returned 963 * @param user the user of the package 964 * 965 * @return an {@link AppUsageLimit} object describing the app time limit containing 966 * the given package with the smallest time remaining, or {@code null} if none exist. 967 * @throws SecurityException when the caller is not the recents app. 968 * @hide 969 */ 970 @Nullable 971 @SystemApi getAppUsageLimit(@onNull String packageName, @NonNull UserHandle user)972 public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName, 973 @NonNull UserHandle user) { 974 try { 975 return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user); 976 } catch (RemoteException re) { 977 throw re.rethrowFromSystemServer(); 978 } 979 } 980 981 /** 982 * Checks if the activity exists and it enabled for a profile. 983 * 984 * <p>The activity may still not be exported, in which case {@link #startMainActivity} will 985 * throw a {@link SecurityException} unless the caller has the same UID as the target app's. 986 * 987 * @param component The activity to check. 988 * @param user The UserHandle of the profile. 989 * 990 * @return true if the activity exists and is enabled. 991 */ isActivityEnabled(ComponentName component, UserHandle user)992 public boolean isActivityEnabled(ComponentName component, UserHandle user) { 993 logErrorForInvalidProfileAccess(user); 994 try { 995 return mService.isActivityEnabled(mContext.getPackageName(), component, user); 996 } catch (RemoteException re) { 997 throw re.rethrowFromSystemServer(); 998 } 999 } 1000 1001 /** 1002 * Returns whether the caller can access the shortcut information. Access is currently 1003 * available to: 1004 * 1005 * <ul> 1006 * <li>The current launcher (or default launcher if there is no set current launcher).</li> 1007 * <li>The currently active voice interaction service.</li> 1008 * </ul> 1009 * 1010 * <p>Note when this method returns {@code false}, it may be a temporary situation because 1011 * the user is trying a new launcher application. The user may decide to change the default 1012 * launcher back to the calling application again, so even if a launcher application loses 1013 * this permission, it does <b>not</b> have to purge pinned shortcut information. 1014 * If the calling launcher application contains pinned shortcuts, they will still work, 1015 * even though the caller no longer has the shortcut host permission. 1016 * 1017 * @throws IllegalStateException when the user is locked. 1018 * 1019 * @see ShortcutManager 1020 */ hasShortcutHostPermission()1021 public boolean hasShortcutHostPermission() { 1022 try { 1023 return mService.hasShortcutHostPermission(mContext.getPackageName()); 1024 } catch (RemoteException re) { 1025 throw re.rethrowFromSystemServer(); 1026 } 1027 } 1028 maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts)1029 private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) { 1030 if (shortcuts == null) { 1031 return null; 1032 } 1033 for (int i = shortcuts.size() - 1; i >= 0; i--) { 1034 final ShortcutInfo si = shortcuts.get(i); 1035 final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext, 1036 si.getDisabledReason()); 1037 if (message != null) { 1038 si.setDisabledMessage(message); 1039 } 1040 } 1041 return shortcuts; 1042 } 1043 1044 /** 1045 * Returns {@link ShortcutInfo}s that match {@code query}. 1046 * 1047 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 1048 * #hasShortcutHostPermission()}. 1049 * 1050 * @param query result includes shortcuts matching this query. 1051 * @param user The UserHandle of the profile. 1052 * 1053 * @return the IDs of {@link ShortcutInfo}s that match the query. 1054 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1055 * is locked or not running. 1056 * 1057 * @see ShortcutManager 1058 */ 1059 @Nullable getShortcuts(@onNull ShortcutQuery query, @NonNull UserHandle user)1060 public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query, 1061 @NonNull UserHandle user) { 1062 logErrorForInvalidProfileAccess(user); 1063 try { 1064 // Note this is the only case we need to update the disabled message for shortcuts 1065 // that weren't restored. 1066 // The restore problem messages are only shown by the user, and publishers will never 1067 // see them. The only other API that the launcher gets shortcuts is the shortcut 1068 // changed callback, but that only returns shortcuts with the "key" information, so 1069 // that won't return disabled message. 1070 return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(), 1071 new ShortcutQueryWrapper(query), user) 1072 .getList()); 1073 } catch (RemoteException e) { 1074 throw e.rethrowFromSystemServer(); 1075 } 1076 } 1077 1078 /** 1079 * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. 1080 */ 1081 @Nullable 1082 @Deprecated getShortcutInfo(@onNull String packageName, @NonNull List<String> ids, @NonNull UserHandle user)1083 public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName, 1084 @NonNull List<String> ids, @NonNull UserHandle user) { 1085 final ShortcutQuery q = new ShortcutQuery(); 1086 q.setPackage(packageName); 1087 q.setShortcutIds(ids); 1088 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 1089 return getShortcuts(q, user); 1090 } 1091 1092 /** 1093 * Pin shortcuts on a package. 1094 * 1095 * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package. 1096 * However, different launchers may have different set of pinned shortcuts. 1097 * 1098 * <p>The calling launcher application must be allowed to access the shortcut information, 1099 * as defined in {@link #hasShortcutHostPermission()}. 1100 * 1101 * @param packageName The target package name. 1102 * @param shortcutIds The IDs of the shortcut to be pinned. 1103 * @param user The UserHandle of the profile. 1104 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1105 * is locked or not running. 1106 * 1107 * @see ShortcutManager 1108 */ pinShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user)1109 public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 1110 @NonNull UserHandle user) { 1111 logErrorForInvalidProfileAccess(user); 1112 try { 1113 mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); 1114 } catch (RemoteException e) { 1115 throw e.rethrowFromSystemServer(); 1116 } 1117 } 1118 1119 /** 1120 * Mark shortcuts as cached for a package. 1121 * 1122 * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts 1123 * in the list will be ignored. 1124 * 1125 * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned 1126 * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same 1127 * shortcut, it can be uncached by any valid caller. 1128 * 1129 * @param packageName The target package name. 1130 * @param shortcutIds The IDs of the shortcut to be cached. 1131 * @param user The UserHandle of the profile. 1132 * @param cacheFlags One of the values in: 1133 * <ul> 1134 * <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS} 1135 * <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS} 1136 * </ul> 1137 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1138 * is locked or not running. 1139 * 1140 * @see ShortcutManager 1141 * 1142 * @hide 1143 */ 1144 @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) cacheShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags)1145 public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 1146 @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags) { 1147 logErrorForInvalidProfileAccess(user); 1148 try { 1149 mService.cacheShortcuts( 1150 mContext.getPackageName(), packageName, shortcutIds, user, cacheFlags); 1151 } catch (RemoteException e) { 1152 throw e.rethrowFromSystemServer(); 1153 } 1154 } 1155 1156 /** 1157 * Remove cached flag from shortcuts for a package. 1158 * 1159 * @param packageName The target package name. 1160 * @param shortcutIds The IDs of the shortcut to be uncached. 1161 * @param user The UserHandle of the profile. 1162 * @param cacheFlags One of the values in: 1163 * <ul> 1164 * <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS} 1165 * <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS} 1166 * </ul> 1167 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1168 * is locked or not running. 1169 * 1170 * @see ShortcutManager 1171 * 1172 * @hide 1173 */ 1174 @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) uncacheShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags)1175 public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 1176 @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags) { 1177 logErrorForInvalidProfileAccess(user); 1178 try { 1179 mService.uncacheShortcuts( 1180 mContext.getPackageName(), packageName, shortcutIds, user, cacheFlags); 1181 } catch (RemoteException e) { 1182 throw e.rethrowFromSystemServer(); 1183 } 1184 } 1185 1186 /** 1187 * @hide kept for testing. 1188 */ 1189 @Deprecated getShortcutIconResId(@onNull ShortcutInfo shortcut)1190 public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { 1191 return shortcut.getIconResourceId(); 1192 } 1193 1194 /** 1195 * @hide kept for testing. 1196 */ 1197 @Deprecated getShortcutIconResId(@onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)1198 public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, 1199 @NonNull UserHandle user) { 1200 final ShortcutQuery q = new ShortcutQuery(); 1201 q.setPackage(packageName); 1202 q.setShortcutIds(Arrays.asList(shortcutId)); 1203 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 1204 final List<ShortcutInfo> shortcuts = getShortcuts(q, user); 1205 1206 return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0; 1207 } 1208 1209 /** 1210 * @hide internal/unit tests only 1211 */ getShortcutIconFd( @onNull ShortcutInfo shortcut)1212 public ParcelFileDescriptor getShortcutIconFd( 1213 @NonNull ShortcutInfo shortcut) { 1214 return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(), 1215 shortcut.getUserId()); 1216 } 1217 1218 /** 1219 * @hide internal/unit tests only 1220 */ getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)1221 public ParcelFileDescriptor getShortcutIconFd( 1222 @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { 1223 return getShortcutIconFd(packageName, shortcutId, user.getIdentifier()); 1224 } 1225 getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, int userId)1226 private ParcelFileDescriptor getShortcutIconFd( 1227 @NonNull String packageName, @NonNull String shortcutId, int userId) { 1228 try { 1229 return mService.getShortcutIconFd(mContext.getPackageName(), 1230 packageName, shortcutId, userId); 1231 } catch (RemoteException e) { 1232 throw e.rethrowFromSystemServer(); 1233 } 1234 } 1235 1236 /** 1237 * @hide internal/unit tests only 1238 */ 1239 @VisibleForTesting getUriShortcutIconFd(@onNull ShortcutInfo shortcut)1240 public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) { 1241 return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId()); 1242 } 1243 getUriShortcutIconFd(@onNull String packageName, @NonNull String shortcutId, int userId)1244 private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName, 1245 @NonNull String shortcutId, int userId) { 1246 String uri = getShortcutIconUri(packageName, shortcutId, userId); 1247 if (uri == null) { 1248 return null; 1249 } 1250 try { 1251 return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r"); 1252 } catch (FileNotFoundException e) { 1253 Log.e(TAG, "Icon file not found: " + uri); 1254 return null; 1255 } 1256 } 1257 getShortcutIconUri(@onNull String packageName, @NonNull String shortcutId, int userId)1258 private String getShortcutIconUri(@NonNull String packageName, 1259 @NonNull String shortcutId, int userId) { 1260 String uri = null; 1261 try { 1262 uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId, 1263 userId); 1264 } catch (RemoteException e) { 1265 throw e.rethrowFromSystemServer(); 1266 } 1267 return uri; 1268 } 1269 1270 /** 1271 * Returns the icon for this shortcut, without any badging for the profile. 1272 * 1273 * <p>The calling launcher application must be allowed to access the shortcut information, 1274 * as defined in {@link #hasShortcutHostPermission()}. 1275 * 1276 * @param density The preferred density of the icon, zero for default density. Use 1277 * density DPI values from {@link DisplayMetrics}. 1278 * 1279 * @return The drawable associated with the shortcut. 1280 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1281 * is locked or not running. 1282 * 1283 * @see ShortcutManager 1284 * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) 1285 * @see DisplayMetrics 1286 */ getShortcutIconDrawable(@onNull ShortcutInfo shortcut, int density)1287 public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { 1288 if (shortcut.hasIconFile()) { 1289 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); 1290 return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap()); 1291 } else if (shortcut.hasIconUri()) { 1292 final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut); 1293 return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap()); 1294 } else if (shortcut.hasIconResource()) { 1295 return loadDrawableResourceFromPackage(shortcut.getPackage(), 1296 shortcut.getIconResourceId(), shortcut.getUserHandle(), density); 1297 } else if (shortcut.getIcon() != null) { 1298 // This happens if a shortcut is pending-approval. 1299 final Icon icon = shortcut.getIcon(); 1300 switch (icon.getType()) { 1301 case Icon.TYPE_RESOURCE: { 1302 return loadDrawableResourceFromPackage(shortcut.getPackage(), 1303 icon.getResId(), shortcut.getUserHandle(), density); 1304 } 1305 case Icon.TYPE_BITMAP: 1306 case Icon.TYPE_ADAPTIVE_BITMAP: { 1307 return icon.loadDrawable(mContext); 1308 } 1309 default: 1310 return null; // Shouldn't happen though. 1311 } 1312 } else { 1313 return null; // Has no icon. 1314 } 1315 } 1316 loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive)1317 private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) { 1318 if (pfd == null) { 1319 return null; 1320 } 1321 try { 1322 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); 1323 if (bmp != null) { 1324 BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp); 1325 if (adaptive) { 1326 return new AdaptiveIconDrawable(null, dr); 1327 } else { 1328 return dr; 1329 } 1330 } 1331 return null; 1332 } finally { 1333 try { 1334 pfd.close(); 1335 } catch (IOException ignore) { 1336 } 1337 } 1338 } 1339 1340 /** 1341 * @hide 1342 */ getShortcutIcon(@onNull ShortcutInfo shortcut)1343 public Icon getShortcutIcon(@NonNull ShortcutInfo shortcut) { 1344 if (shortcut.hasIconFile()) { 1345 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); 1346 if (pfd == null) { 1347 return null; 1348 } 1349 try { 1350 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); 1351 if (bmp != null) { 1352 if (shortcut.hasAdaptiveBitmap()) { 1353 return Icon.createWithAdaptiveBitmap(bmp); 1354 } else { 1355 return Icon.createWithBitmap(bmp); 1356 } 1357 } 1358 return null; 1359 } finally { 1360 try { 1361 pfd.close(); 1362 } catch (IOException ignore) { 1363 } 1364 } 1365 } else if (shortcut.hasIconUri()) { 1366 String uri = getShortcutIconUri(shortcut.getPackage(), shortcut.getId(), 1367 shortcut.getUserId()); 1368 if (uri == null) { 1369 return null; 1370 } 1371 if (shortcut.hasAdaptiveBitmap()) { 1372 return Icon.createWithAdaptiveBitmapContentUri(uri); 1373 } else { 1374 return Icon.createWithContentUri(uri); 1375 } 1376 } else if (shortcut.hasIconResource()) { 1377 return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId()); 1378 } else { 1379 return shortcut.getIcon(); 1380 } 1381 } 1382 loadDrawableResourceFromPackage(String packageName, int resId, UserHandle user, int density)1383 private Drawable loadDrawableResourceFromPackage(String packageName, int resId, 1384 UserHandle user, int density) { 1385 try { 1386 if (resId == 0) { 1387 return null; // Shouldn't happen but just in case. 1388 } 1389 final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user); 1390 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai); 1391 return res.getDrawableForDensity(resId, density); 1392 } catch (NameNotFoundException | Resources.NotFoundException e) { 1393 return null; 1394 } 1395 } 1396 1397 /** 1398 * Returns the shortcut icon with badging appropriate for the profile. 1399 * 1400 * <p>The calling launcher application must be allowed to access the shortcut information, 1401 * as defined in {@link #hasShortcutHostPermission()}. 1402 * 1403 * @param density Optional density for the icon, or 0 to use the default density. Use 1404 * @return A badged icon for the shortcut. 1405 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1406 * is locked or not running. 1407 * 1408 * @see ShortcutManager 1409 * @see #getShortcutIconDrawable(ShortcutInfo, int) 1410 * @see DisplayMetrics 1411 */ getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density)1412 public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) { 1413 final Drawable originalIcon = getShortcutIconDrawable(shortcut, density); 1414 1415 return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon( 1416 originalIcon, shortcut.getUserHandle()); 1417 } 1418 1419 /** 1420 * Starts a shortcut. 1421 * 1422 * <p>The calling launcher application must be allowed to access the shortcut information, 1423 * as defined in {@link #hasShortcutHostPermission()}. 1424 * 1425 * @param packageName The target shortcut package name. 1426 * @param shortcutId The target shortcut ID. 1427 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1428 * @param startActivityOptions Options to pass to startActivity. 1429 * @param user The UserHandle of the profile. 1430 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1431 * is locked or not running. 1432 * 1433 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1434 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1435 */ startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user)1436 public void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1437 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1438 @NonNull UserHandle user) { 1439 logErrorForInvalidProfileAccess(user); 1440 1441 startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, 1442 user.getIdentifier()); 1443 } 1444 1445 /** 1446 * Launches a shortcut. 1447 * 1448 * <p>The calling launcher application must be allowed to access the shortcut information, 1449 * as defined in {@link #hasShortcutHostPermission()}. 1450 * 1451 * @param shortcut The target shortcut. 1452 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1453 * @param startActivityOptions Options to pass to startActivity. 1454 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1455 * is locked or not running. 1456 * 1457 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1458 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1459 */ startShortcut(@onNull ShortcutInfo shortcut, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions)1460 public void startShortcut(@NonNull ShortcutInfo shortcut, 1461 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { 1462 startShortcut(shortcut.getPackage(), shortcut.getId(), 1463 sourceBounds, startActivityOptions, 1464 shortcut.getUserId()); 1465 } 1466 1467 @UnsupportedAppUsage startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, int userId)1468 private void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1469 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1470 int userId) { 1471 try { 1472 final boolean success = mService.startShortcut(mContext.getPackageName(), packageName, 1473 null /* default featureId */, shortcutId, sourceBounds, startActivityOptions, 1474 userId); 1475 if (!success) { 1476 throw new ActivityNotFoundException("Shortcut could not be started"); 1477 } 1478 } catch (RemoteException e) { 1479 throw e.rethrowFromSystemServer(); 1480 } 1481 } 1482 1483 /** 1484 * Registers a callback for changes to packages in this user and managed profiles. 1485 * 1486 * @param callback The callback to register. 1487 */ registerCallback(Callback callback)1488 public void registerCallback(Callback callback) { 1489 registerCallback(callback, null); 1490 } 1491 1492 /** 1493 * Registers a callback for changes to packages in this user and managed profiles. 1494 * 1495 * @param callback The callback to register. 1496 * @param handler that should be used to post callbacks on, may be null. 1497 */ registerCallback(Callback callback, Handler handler)1498 public void registerCallback(Callback callback, Handler handler) { 1499 synchronized (this) { 1500 if (callback != null && findCallbackLocked(callback) < 0) { 1501 boolean addedFirstCallback = mCallbacks.size() == 0; 1502 addCallbackLocked(callback, handler); 1503 if (addedFirstCallback) { 1504 try { 1505 mService.addOnAppsChangedListener(mContext.getPackageName(), 1506 mAppsChangedListener); 1507 } catch (RemoteException re) { 1508 throw re.rethrowFromSystemServer(); 1509 } 1510 } 1511 } 1512 } 1513 } 1514 1515 /** 1516 * Unregisters a callback that was previously registered. 1517 * 1518 * @param callback The callback to unregister. 1519 * @see #registerCallback(Callback) 1520 */ unregisterCallback(Callback callback)1521 public void unregisterCallback(Callback callback) { 1522 synchronized (this) { 1523 removeCallbackLocked(callback); 1524 if (mCallbacks.size() == 0) { 1525 try { 1526 mService.removeOnAppsChangedListener(mAppsChangedListener); 1527 } catch (RemoteException re) { 1528 throw re.rethrowFromSystemServer(); 1529 } 1530 } 1531 } 1532 } 1533 1534 /** @return position in mCallbacks for callback or -1 if not present. */ findCallbackLocked(Callback callback)1535 private int findCallbackLocked(Callback callback) { 1536 if (callback == null) { 1537 throw new IllegalArgumentException("Callback cannot be null"); 1538 } 1539 final int size = mCallbacks.size(); 1540 for (int i = 0; i < size; ++i) { 1541 if (mCallbacks.get(i).mCallback == callback) { 1542 return i; 1543 } 1544 } 1545 return -1; 1546 } 1547 removeCallbackLocked(Callback callback)1548 private void removeCallbackLocked(Callback callback) { 1549 int pos = findCallbackLocked(callback); 1550 if (pos >= 0) { 1551 mCallbacks.remove(pos); 1552 } 1553 } 1554 addCallbackLocked(Callback callback, Handler handler)1555 private void addCallbackLocked(Callback callback, Handler handler) { 1556 // Remove if already present. 1557 removeCallbackLocked(callback); 1558 if (handler == null) { 1559 handler = new Handler(); 1560 } 1561 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback); 1562 mCallbacks.add(toAdd); 1563 } 1564 1565 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() { 1566 1567 @Override 1568 public void onPackageRemoved(UserHandle user, String packageName) 1569 throws RemoteException { 1570 if (DEBUG) { 1571 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName); 1572 } 1573 synchronized (LauncherApps.this) { 1574 for (CallbackMessageHandler callback : mCallbacks) { 1575 callback.postOnPackageRemoved(packageName, user); 1576 } 1577 } 1578 } 1579 1580 @Override 1581 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException { 1582 if (DEBUG) { 1583 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName); 1584 } 1585 synchronized (LauncherApps.this) { 1586 for (CallbackMessageHandler callback : mCallbacks) { 1587 callback.postOnPackageChanged(packageName, user); 1588 } 1589 } 1590 } 1591 1592 @Override 1593 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException { 1594 if (DEBUG) { 1595 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName); 1596 } 1597 synchronized (LauncherApps.this) { 1598 for (CallbackMessageHandler callback : mCallbacks) { 1599 callback.postOnPackageAdded(packageName, user); 1600 } 1601 } 1602 } 1603 1604 @Override 1605 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) 1606 throws RemoteException { 1607 if (DEBUG) { 1608 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames); 1609 } 1610 synchronized (LauncherApps.this) { 1611 for (CallbackMessageHandler callback : mCallbacks) { 1612 callback.postOnPackagesAvailable(packageNames, user, replacing); 1613 } 1614 } 1615 } 1616 1617 @Override 1618 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing) 1619 throws RemoteException { 1620 if (DEBUG) { 1621 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames); 1622 } 1623 synchronized (LauncherApps.this) { 1624 for (CallbackMessageHandler callback : mCallbacks) { 1625 callback.postOnPackagesUnavailable(packageNames, user, replacing); 1626 } 1627 } 1628 } 1629 1630 @Override 1631 public void onPackagesSuspended(UserHandle user, String[] packageNames, 1632 Bundle launcherExtras) 1633 throws RemoteException { 1634 if (DEBUG) { 1635 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames); 1636 } 1637 synchronized (LauncherApps.this) { 1638 for (CallbackMessageHandler callback : mCallbacks) { 1639 callback.postOnPackagesSuspended(packageNames, launcherExtras, user); 1640 } 1641 } 1642 } 1643 1644 @Override 1645 public void onPackagesUnsuspended(UserHandle user, String[] packageNames) 1646 throws RemoteException { 1647 if (DEBUG) { 1648 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames); 1649 } 1650 synchronized (LauncherApps.this) { 1651 for (CallbackMessageHandler callback : mCallbacks) { 1652 callback.postOnPackagesUnsuspended(packageNames, user); 1653 } 1654 } 1655 } 1656 1657 @Override 1658 public void onShortcutChanged(UserHandle user, String packageName, 1659 ParceledListSlice shortcuts) { 1660 if (DEBUG) { 1661 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName); 1662 } 1663 final List<ShortcutInfo> list = shortcuts.getList(); 1664 synchronized (LauncherApps.this) { 1665 for (CallbackMessageHandler callback : mCallbacks) { 1666 callback.postOnShortcutChanged(packageName, user, list); 1667 } 1668 } 1669 } 1670 }; 1671 1672 private static class CallbackMessageHandler extends Handler { 1673 private static final int MSG_ADDED = 1; 1674 private static final int MSG_REMOVED = 2; 1675 private static final int MSG_CHANGED = 3; 1676 private static final int MSG_AVAILABLE = 4; 1677 private static final int MSG_UNAVAILABLE = 5; 1678 private static final int MSG_SUSPENDED = 6; 1679 private static final int MSG_UNSUSPENDED = 7; 1680 private static final int MSG_SHORTCUT_CHANGED = 8; 1681 1682 private LauncherApps.Callback mCallback; 1683 1684 private static class CallbackInfo { 1685 String[] packageNames; 1686 String packageName; 1687 Bundle launcherExtras; 1688 boolean replacing; 1689 UserHandle user; 1690 List<ShortcutInfo> shortcuts; 1691 } 1692 CallbackMessageHandler(Looper looper, LauncherApps.Callback callback)1693 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) { 1694 super(looper, null, true); 1695 mCallback = callback; 1696 } 1697 1698 @Override handleMessage(Message msg)1699 public void handleMessage(Message msg) { 1700 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) { 1701 return; 1702 } 1703 CallbackInfo info = (CallbackInfo) msg.obj; 1704 switch (msg.what) { 1705 case MSG_ADDED: 1706 mCallback.onPackageAdded(info.packageName, info.user); 1707 break; 1708 case MSG_REMOVED: 1709 mCallback.onPackageRemoved(info.packageName, info.user); 1710 break; 1711 case MSG_CHANGED: 1712 mCallback.onPackageChanged(info.packageName, info.user); 1713 break; 1714 case MSG_AVAILABLE: 1715 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing); 1716 break; 1717 case MSG_UNAVAILABLE: 1718 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing); 1719 break; 1720 case MSG_SUSPENDED: 1721 mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras 1722 ); 1723 break; 1724 case MSG_UNSUSPENDED: 1725 mCallback.onPackagesUnsuspended(info.packageNames, info.user); 1726 break; 1727 case MSG_SHORTCUT_CHANGED: 1728 mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user); 1729 break; 1730 } 1731 } 1732 postOnPackageAdded(String packageName, UserHandle user)1733 public void postOnPackageAdded(String packageName, UserHandle user) { 1734 CallbackInfo info = new CallbackInfo(); 1735 info.packageName = packageName; 1736 info.user = user; 1737 obtainMessage(MSG_ADDED, info).sendToTarget(); 1738 } 1739 postOnPackageRemoved(String packageName, UserHandle user)1740 public void postOnPackageRemoved(String packageName, UserHandle user) { 1741 CallbackInfo info = new CallbackInfo(); 1742 info.packageName = packageName; 1743 info.user = user; 1744 obtainMessage(MSG_REMOVED, info).sendToTarget(); 1745 } 1746 postOnPackageChanged(String packageName, UserHandle user)1747 public void postOnPackageChanged(String packageName, UserHandle user) { 1748 CallbackInfo info = new CallbackInfo(); 1749 info.packageName = packageName; 1750 info.user = user; 1751 obtainMessage(MSG_CHANGED, info).sendToTarget(); 1752 } 1753 postOnPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)1754 public void postOnPackagesAvailable(String[] packageNames, UserHandle user, 1755 boolean replacing) { 1756 CallbackInfo info = new CallbackInfo(); 1757 info.packageNames = packageNames; 1758 info.replacing = replacing; 1759 info.user = user; 1760 obtainMessage(MSG_AVAILABLE, info).sendToTarget(); 1761 } 1762 postOnPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)1763 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user, 1764 boolean replacing) { 1765 CallbackInfo info = new CallbackInfo(); 1766 info.packageNames = packageNames; 1767 info.replacing = replacing; 1768 info.user = user; 1769 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); 1770 } 1771 postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, UserHandle user)1772 public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, 1773 UserHandle user) { 1774 CallbackInfo info = new CallbackInfo(); 1775 info.packageNames = packageNames; 1776 info.user = user; 1777 info.launcherExtras = launcherExtras; 1778 obtainMessage(MSG_SUSPENDED, info).sendToTarget(); 1779 } 1780 postOnPackagesUnsuspended(String[] packageNames, UserHandle user)1781 public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) { 1782 CallbackInfo info = new CallbackInfo(); 1783 info.packageNames = packageNames; 1784 info.user = user; 1785 obtainMessage(MSG_UNSUSPENDED, info).sendToTarget(); 1786 } 1787 postOnShortcutChanged(String packageName, UserHandle user, List<ShortcutInfo> shortcuts)1788 public void postOnShortcutChanged(String packageName, UserHandle user, 1789 List<ShortcutInfo> shortcuts) { 1790 CallbackInfo info = new CallbackInfo(); 1791 info.packageName = packageName; 1792 info.user = user; 1793 info.shortcuts = shortcuts; 1794 obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget(); 1795 } 1796 } 1797 1798 /** 1799 * Register a callback to watch for session lifecycle events in this user and managed profiles. 1800 * @param callback The callback to register. 1801 * @param executor {@link Executor} to handle the callbacks, cannot be null. 1802 * 1803 * @see PackageInstaller#registerSessionCallback(SessionCallback) 1804 */ registerPackageInstallerSessionCallback( @onNull @allbackExecutor Executor executor, @NonNull SessionCallback callback)1805 public void registerPackageInstallerSessionCallback( 1806 @NonNull @CallbackExecutor Executor executor, @NonNull SessionCallback callback) { 1807 if (executor == null) { 1808 throw new NullPointerException("Executor must not be null"); 1809 } 1810 1811 synchronized (mDelegates) { 1812 final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback, 1813 executor); 1814 try { 1815 mService.registerPackageInstallerCallback(mContext.getPackageName(), 1816 delegate); 1817 } catch (RemoteException e) { 1818 throw e.rethrowFromSystemServer(); 1819 } 1820 mDelegates.add(delegate); 1821 } 1822 } 1823 1824 /** 1825 * Unregisters a callback that was previously registered. 1826 * 1827 * @param callback The callback to unregister. 1828 * @see #registerPackageInstallerSessionCallback(Executor, SessionCallback) 1829 */ unregisterPackageInstallerSessionCallback(@onNull SessionCallback callback)1830 public void unregisterPackageInstallerSessionCallback(@NonNull SessionCallback callback) { 1831 synchronized (mDelegates) { 1832 for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) { 1833 final SessionCallbackDelegate delegate = i.next(); 1834 if (delegate.mCallback == callback) { 1835 mPm.getPackageInstaller().unregisterSessionCallback(delegate.mCallback); 1836 i.remove(); 1837 } 1838 } 1839 } 1840 } 1841 1842 /** 1843 * Return list of all known install sessions in this user and managed profiles, regardless 1844 * of the installer. 1845 * 1846 * @see PackageInstaller#getAllSessions() 1847 */ getAllPackageInstallerSessions()1848 public @NonNull List<SessionInfo> getAllPackageInstallerSessions() { 1849 try { 1850 return mService.getAllSessions(mContext.getPackageName()).getList(); 1851 } catch (RemoteException e) { 1852 throw e.rethrowFromSystemServer(); 1853 } 1854 } 1855 1856 /** 1857 * Register a callback to watch for shortcut change events in this user and managed profiles. 1858 * 1859 * @param callback The callback to register. 1860 * @param query {@link ShortcutQuery} to match and filter the shortcut events. Only matching 1861 * shortcuts will be returned by the callback. 1862 * @param executor {@link Executor} to handle the callbacks. To dispatch callbacks to the main 1863 * thread of your application, you can use {@link android.content.Context#getMainExecutor()}. 1864 * 1865 * @hide 1866 */ registerShortcutChangeCallback(@onNull ShortcutChangeCallback callback, @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor)1867 public void registerShortcutChangeCallback(@NonNull ShortcutChangeCallback callback, 1868 @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor) { 1869 Objects.requireNonNull(callback, "Callback cannot be null"); 1870 Objects.requireNonNull(query, "Query cannot be null"); 1871 Objects.requireNonNull(executor, "Executor cannot be null"); 1872 1873 synchronized (mShortcutChangeCallbacks) { 1874 IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback); 1875 mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy)); 1876 try { 1877 mService.registerShortcutChangeCallback(mContext.getPackageName(), 1878 new ShortcutQueryWrapper(query), proxy); 1879 } catch (RemoteException e) { 1880 throw e.rethrowFromSystemServer(); 1881 } 1882 } 1883 } 1884 1885 /** 1886 * Unregisters a callback that was previously registered. 1887 * @see #registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, Executor) 1888 * 1889 * @param callback Callback to be unregistered. 1890 * 1891 * @hide 1892 */ unregisterShortcutChangeCallback(@onNull ShortcutChangeCallback callback)1893 public void unregisterShortcutChangeCallback(@NonNull ShortcutChangeCallback callback) { 1894 Objects.requireNonNull(callback, "Callback cannot be null"); 1895 1896 synchronized (mShortcutChangeCallbacks) { 1897 if (mShortcutChangeCallbacks.containsKey(callback)) { 1898 IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second; 1899 try { 1900 mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy); 1901 } catch (RemoteException e) { 1902 throw e.rethrowFromSystemServer(); 1903 } 1904 } 1905 } 1906 } 1907 1908 /** 1909 * A helper method to extract a {@link PinItemRequest} set to 1910 * the {@link #EXTRA_PIN_ITEM_REQUEST} extra. 1911 */ getPinItemRequest(Intent intent)1912 public PinItemRequest getPinItemRequest(Intent intent) { 1913 return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST); 1914 } 1915 1916 /** 1917 * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with 1918 * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent 1919 * respectively to the default launcher app. 1920 * 1921 * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type.</h3> 1922 * 1923 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1924 * {@link ShortcutInfo}. If the launcher accepts a request, call {@link #accept()}, 1925 * or {@link #accept(Bundle)} with a null or empty Bundle. No options are defined for 1926 * pin-shortcuts requests. 1927 * 1928 * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type. 1929 * 1930 * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in 1931 * which case {@link ShortcutInfo#isPinned()} returns true. This means the user wants to create 1932 * another pinned shortcut for a shortcut that's already pinned. If the launcher accepts it, 1933 * {@link #accept()} must still be called even though the shortcut is already pinned, and 1934 * create a new pinned shortcut icon for it. 1935 * 1936 * <p>See also {@link ShortcutManager} for more details. 1937 * 1938 * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type.</h3> 1939 * 1940 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1941 * an AppWidget. If the launcher accepts a request, call {@link #accept(Bundle)} with 1942 * the appwidget integer ID set to the 1943 * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra. 1944 * 1945 * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null 1946 * {@link AppWidgetProviderInfo} for this type. 1947 * 1948 * <p>See also {@link AppWidgetManager} for more details. 1949 * 1950 * @see #EXTRA_PIN_ITEM_REQUEST 1951 * @see #getPinItemRequest(Intent) 1952 */ 1953 public static final class PinItemRequest implements Parcelable { 1954 1955 /** This is a request to pin shortcut. */ 1956 public static final int REQUEST_TYPE_SHORTCUT = 1; 1957 1958 /** This is a request to pin app widget. */ 1959 public static final int REQUEST_TYPE_APPWIDGET = 2; 1960 1961 /** @hide */ 1962 @IntDef(prefix = { "REQUEST_TYPE_" }, value = { 1963 REQUEST_TYPE_SHORTCUT, 1964 REQUEST_TYPE_APPWIDGET 1965 }) 1966 @Retention(RetentionPolicy.SOURCE) 1967 public @interface RequestType {} 1968 1969 private final int mRequestType; 1970 private final IPinItemRequest mInner; 1971 1972 /** 1973 * @hide 1974 */ PinItemRequest(IPinItemRequest inner, int type)1975 public PinItemRequest(IPinItemRequest inner, int type) { 1976 mInner = inner; 1977 mRequestType = type; 1978 } 1979 1980 /** 1981 * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants. 1982 * 1983 * @return one of the {@code REQUEST_TYPE_} constants. 1984 */ 1985 @RequestType getRequestType()1986 public int getRequestType() { 1987 return mRequestType; 1988 } 1989 1990 /** 1991 * {@link ShortcutInfo} sent by the requesting app. 1992 * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a 1993 * different request type. 1994 * 1995 * @return requested {@link ShortcutInfo} when a request is of the 1996 * {@link #REQUEST_TYPE_SHORTCUT} type. Null otherwise. 1997 */ 1998 @Nullable getShortcutInfo()1999 public ShortcutInfo getShortcutInfo() { 2000 try { 2001 return mInner.getShortcutInfo(); 2002 } catch (RemoteException e) { 2003 throw e.rethrowAsRuntimeException(); 2004 } 2005 } 2006 2007 /** 2008 * {@link AppWidgetProviderInfo} sent by the requesting app. 2009 * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a 2010 * different request type. 2011 * 2012 * <p>Launcher should not show any configuration activity associated with the provider, and 2013 * assume that the widget is already fully configured. Upon accepting the widget, it should 2014 * pass the widgetId in {@link #accept(Bundle)}. 2015 * 2016 * @return requested {@link AppWidgetProviderInfo} when a request is of the 2017 * {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise. 2018 */ 2019 @Nullable getAppWidgetProviderInfo(Context context)2020 public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) { 2021 try { 2022 final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo(); 2023 if (info == null) { 2024 return null; 2025 } 2026 info.updateDimensions(context.getResources().getDisplayMetrics()); 2027 return info; 2028 } catch (RemoteException e) { 2029 throw e.rethrowAsRuntimeException(); 2030 } 2031 } 2032 2033 /** 2034 * Any extras sent by the requesting app. 2035 * 2036 * @return For a shortcut request, this method always return null. For an AppWidget 2037 * request, this method returns the extras passed to the 2038 * {@link android.appwidget.AppWidgetManager#requestPinAppWidget( 2039 * ComponentName, Bundle, PendingIntent)} API. See {@link AppWidgetManager} for details. 2040 */ 2041 @Nullable getExtras()2042 public Bundle getExtras() { 2043 try { 2044 return mInner.getExtras(); 2045 } catch (RemoteException e) { 2046 throw e.rethrowAsRuntimeException(); 2047 } 2048 } 2049 2050 /** 2051 * Return whether a request is still valid. 2052 * 2053 * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called. 2054 */ isValid()2055 public boolean isValid() { 2056 try { 2057 return mInner.isValid(); 2058 } catch (RemoteException e) { 2059 return false; 2060 } 2061 } 2062 2063 /** 2064 * Called by the receiving launcher app when the user accepts the request. 2065 * 2066 * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request. 2067 * 2068 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 2069 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 2070 * already been canceled, in which case the launcher must not pin the requested item. 2071 */ accept(@ullable Bundle options)2072 public boolean accept(@Nullable Bundle options) { 2073 try { 2074 return mInner.accept(options); 2075 } catch (RemoteException e) { 2076 throw e.rethrowFromSystemServer(); 2077 } 2078 } 2079 2080 /** 2081 * Called by the receiving launcher app when the user accepts the request, with no options. 2082 * 2083 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 2084 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 2085 * already been canceled, in which case the launcher must not pin the requested item. 2086 */ accept()2087 public boolean accept() { 2088 return accept(/* options= */ null); 2089 } 2090 PinItemRequest(Parcel source)2091 private PinItemRequest(Parcel source) { 2092 final ClassLoader cl = getClass().getClassLoader(); 2093 2094 mRequestType = source.readInt(); 2095 mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder()); 2096 } 2097 2098 @Override writeToParcel(Parcel dest, int flags)2099 public void writeToParcel(Parcel dest, int flags) { 2100 dest.writeInt(mRequestType); 2101 dest.writeStrongBinder(mInner.asBinder()); 2102 } 2103 2104 public static final @android.annotation.NonNull Creator<PinItemRequest> CREATOR = 2105 new Creator<PinItemRequest>() { 2106 public PinItemRequest createFromParcel(Parcel source) { 2107 return new PinItemRequest(source); 2108 } 2109 public PinItemRequest[] newArray(int size) { 2110 return new PinItemRequest[size]; 2111 } 2112 }; 2113 2114 @Override describeContents()2115 public int describeContents() { 2116 return 0; 2117 } 2118 } 2119 2120 /** 2121 * A class that encapsulates information about the usage limit set for an app or 2122 * a group of apps. 2123 * 2124 * <p>The launcher can query specifics about the usage limit such as how much usage time 2125 * the limit has and how much of the total usage time is remaining via the APIs available 2126 * in this class. 2127 * 2128 * @see #getAppUsageLimit(String, UserHandle) 2129 * @hide 2130 */ 2131 @SystemApi 2132 public static final class AppUsageLimit implements Parcelable { 2133 private final long mTotalUsageLimit; 2134 private final long mUsageRemaining; 2135 2136 /** @hide */ AppUsageLimit(long totalUsageLimit, long usageRemaining)2137 public AppUsageLimit(long totalUsageLimit, long usageRemaining) { 2138 this.mTotalUsageLimit = totalUsageLimit; 2139 this.mUsageRemaining = usageRemaining; 2140 } 2141 2142 /** 2143 * Returns the total usage limit in milliseconds set for an app or a group of apps. 2144 * 2145 * @return the total usage limit in milliseconds 2146 */ getTotalUsageLimit()2147 public long getTotalUsageLimit() { 2148 return mTotalUsageLimit; 2149 } 2150 2151 /** 2152 * Returns the usage remaining in milliseconds for an app or the group of apps 2153 * this limit refers to. 2154 * 2155 * @return the usage remaining in milliseconds 2156 */ getUsageRemaining()2157 public long getUsageRemaining() { 2158 return mUsageRemaining; 2159 } 2160 AppUsageLimit(Parcel source)2161 private AppUsageLimit(Parcel source) { 2162 mTotalUsageLimit = source.readLong(); 2163 mUsageRemaining = source.readLong(); 2164 } 2165 2166 public static final @android.annotation.NonNull Creator<AppUsageLimit> CREATOR = new Creator<AppUsageLimit>() { 2167 @Override 2168 public AppUsageLimit createFromParcel(Parcel source) { 2169 return new AppUsageLimit(source); 2170 } 2171 2172 @Override 2173 public AppUsageLimit[] newArray(int size) { 2174 return new AppUsageLimit[size]; 2175 } 2176 }; 2177 2178 @Override describeContents()2179 public int describeContents() { 2180 return 0; 2181 } 2182 2183 @Override writeToParcel(Parcel dest, int flags)2184 public void writeToParcel(Parcel dest, int flags) { 2185 dest.writeLong(mTotalUsageLimit); 2186 dest.writeLong(mUsageRemaining); 2187 } 2188 } 2189 } 2190