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