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