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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.annotation.SystemService; 25 import android.annotation.TestApi; 26 import android.app.PendingIntent; 27 import android.appwidget.AppWidgetManager; 28 import android.appwidget.AppWidgetProviderInfo; 29 import android.content.ActivityNotFoundException; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentSender; 34 import android.content.pm.PackageManager.ApplicationInfoFlags; 35 import android.content.pm.PackageManager.NameNotFoundException; 36 import android.content.res.Resources; 37 import android.graphics.Bitmap; 38 import android.graphics.BitmapFactory; 39 import android.graphics.Rect; 40 import android.graphics.drawable.AdaptiveIconDrawable; 41 import android.graphics.drawable.BitmapDrawable; 42 import android.graphics.drawable.Drawable; 43 import android.graphics.drawable.Icon; 44 import android.os.Bundle; 45 import android.os.Handler; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.Parcel; 49 import android.os.ParcelFileDescriptor; 50 import android.os.Parcelable; 51 import android.os.RemoteException; 52 import android.os.ServiceManager; 53 import android.os.UserHandle; 54 import android.os.UserManager; 55 import android.util.DisplayMetrics; 56 import android.util.Log; 57 58 import com.android.internal.util.Preconditions; 59 60 import java.io.IOException; 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collections; 66 import java.util.List; 67 68 /** 69 * Class for retrieving a list of launchable activities for the current user and any associated 70 * managed profiles that are visible to the current user, which can be retrieved with 71 * {@link #getProfiles}. This is mainly for use by launchers. 72 * 73 * Apps can be queried for each user profile. 74 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register 75 * for package changes here. 76 * <p> 77 * To watch for managed profiles being added or removed, register for the following broadcasts: 78 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}. 79 * <p> 80 * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the 81 * main profile. Apps can only access profiles returned by {@link #getProfiles()}. 82 */ 83 @SystemService(Context.LAUNCHER_APPS_SERVICE) 84 public class LauncherApps { 85 86 static final String TAG = "LauncherApps"; 87 static final boolean DEBUG = false; 88 89 /** 90 * Activity Action: For the default launcher to show the confirmation dialog to create 91 * a pinned shortcut. 92 * 93 * <p>See the {@link ShortcutManager} javadoc for details. 94 * 95 * <p> 96 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 97 * and call {@link PinItemRequest#accept(Bundle)} 98 * if the user accepts. If the user doesn't accept, no further action is required. 99 * 100 * @see #EXTRA_PIN_ITEM_REQUEST 101 */ 102 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 103 public static final String ACTION_CONFIRM_PIN_SHORTCUT = 104 "android.content.pm.action.CONFIRM_PIN_SHORTCUT"; 105 106 /** 107 * Activity Action: For the default launcher to show the confirmation dialog to create 108 * a pinned app widget. 109 * 110 * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for 111 * details. 112 * 113 * <p> 114 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 115 * and call {@link PinItemRequest#accept(Bundle)} 116 * if the user accepts. If the user doesn't accept, no further action is required. 117 * 118 * @see #EXTRA_PIN_ITEM_REQUEST 119 */ 120 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 121 public static final String ACTION_CONFIRM_PIN_APPWIDGET = 122 "android.content.pm.action.CONFIRM_PIN_APPWIDGET"; 123 124 /** 125 * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} & {@link #ACTION_CONFIRM_PIN_APPWIDGET} 126 * containing a {@link PinItemRequest} of appropriate type asked to pin. 127 * 128 * <p>A helper function {@link #getPinItemRequest(Intent)} can be used 129 * instead of using this constant directly. 130 * 131 * @see #ACTION_CONFIRM_PIN_SHORTCUT 132 * @see #ACTION_CONFIRM_PIN_APPWIDGET 133 */ 134 public static final String EXTRA_PIN_ITEM_REQUEST = 135 "android.content.pm.extra.PIN_ITEM_REQUEST"; 136 137 private final Context mContext; 138 private final ILauncherApps mService; 139 private final PackageManager mPm; 140 private final UserManager mUserManager; 141 142 private List<CallbackMessageHandler> mCallbacks 143 = new ArrayList<CallbackMessageHandler>(); 144 145 /** 146 * Callbacks for package changes to this and related managed profiles. 147 */ 148 public static abstract class Callback { 149 /** 150 * Indicates that a package was removed from the specified profile. 151 * 152 * If a package is removed while being updated onPackageChanged will be 153 * called instead. 154 * 155 * @param packageName The name of the package that was removed. 156 * @param user The UserHandle of the profile that generated the change. 157 */ onPackageRemoved(String packageName, UserHandle user)158 abstract public void onPackageRemoved(String packageName, UserHandle user); 159 160 /** 161 * Indicates that a package was added to the specified profile. 162 * 163 * If a package is added while being updated then onPackageChanged will be 164 * called instead. 165 * 166 * @param packageName The name of the package that was added. 167 * @param user The UserHandle of the profile that generated the change. 168 */ onPackageAdded(String packageName, UserHandle user)169 abstract public void onPackageAdded(String packageName, UserHandle user); 170 171 /** 172 * Indicates that a package was modified in the specified profile. 173 * This can happen, for example, when the package is updated or when 174 * one or more components are enabled or disabled. 175 * 176 * @param packageName The name of the package that has changed. 177 * @param user The UserHandle of the profile that generated the change. 178 */ onPackageChanged(String packageName, UserHandle user)179 abstract public void onPackageChanged(String packageName, UserHandle user); 180 181 /** 182 * Indicates that one or more packages have become available. For 183 * example, this can happen when a removable storage card has 184 * reappeared. 185 * 186 * @param packageNames The names of the packages that have become 187 * available. 188 * @param user The UserHandle of the profile that generated the change. 189 * @param replacing Indicates whether these packages are replacing 190 * existing ones. 191 */ onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)192 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user, 193 boolean replacing); 194 195 /** 196 * Indicates that one or more packages have become unavailable. For 197 * example, this can happen when a removable storage card has been 198 * removed. 199 * 200 * @param packageNames The names of the packages that have become 201 * unavailable. 202 * @param user The UserHandle of the profile that generated the change. 203 * @param replacing Indicates whether the packages are about to be 204 * replaced with new versions. 205 */ onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)206 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user, 207 boolean replacing); 208 209 /** 210 * Indicates that one or more packages have been suspended. For 211 * example, this can happen when a Device Administrator suspends 212 * an applicaton. 213 * 214 * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher, 215 * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will 216 * not receive this callback. 217 * 218 * @param packageNames The names of the packages that have just been 219 * suspended. 220 * @param user The UserHandle of the profile that generated the change. 221 */ onPackagesSuspended(String[] packageNames, UserHandle user)222 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 223 } 224 225 /** 226 * Indicates that one or more packages have been suspended. A device administrator or an app 227 * with {@code android.permission.SUSPEND_APPS} can do this. 228 * 229 * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can 230 * optionally provide a {@link Bundle} of extra information that it deems helpful for the 231 * launcher to handle the suspended state of these packages. The contents of this 232 * {@link Bundle} are supposed to be a contract between the suspending app and the launcher. 233 * 234 * @param packageNames The names of the packages that have just been suspended. 235 * @param user the user for which the given packages were suspended. 236 * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the 237 * system, {@code null} otherwise. 238 * @see PackageManager#isPackageSuspended() 239 * @see #getSuspendedPackageLauncherExtras(String, UserHandle) 240 */ onPackagesSuspended(String[] packageNames, UserHandle user, @Nullable Bundle launcherExtras)241 public void onPackagesSuspended(String[] packageNames, UserHandle user, 242 @Nullable Bundle launcherExtras) { 243 onPackagesSuspended(packageNames, user); 244 } 245 246 /** 247 * Indicates that one or more packages have been unsuspended. For 248 * example, this can happen when a Device Administrator unsuspends 249 * an applicaton. 250 * 251 * @param packageNames The names of the packages that have just been 252 * unsuspended. 253 * @param user The UserHandle of the profile that generated the change. 254 */ onPackagesUnsuspended(String[] packageNames, UserHandle user)255 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 256 } 257 258 /** 259 * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest) 260 * have been added, updated or removed. 261 * 262 * <p>Only the applications that are allowed to access the shortcut information, 263 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 264 * 265 * @param packageName The name of the package that has the shortcuts. 266 * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned). 267 * Only "key" information will be provided, as defined in 268 * {@link ShortcutInfo#hasKeyFieldsOnly()}. 269 * @param user The UserHandle of the profile that generated the change. 270 * 271 * @see ShortcutManager 272 */ onShortcutsChanged(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)273 public void onShortcutsChanged(@NonNull String packageName, 274 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 275 } 276 } 277 278 /** 279 * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}. 280 */ 281 public static class ShortcutQuery { 282 /** 283 * Include dynamic shortcuts in the result. 284 */ 285 public static final int FLAG_MATCH_DYNAMIC = 1 << 0; 286 287 /** @hide kept for unit tests */ 288 @Deprecated 289 public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC; 290 291 /** 292 * Include pinned shortcuts in the result. 293 * 294 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 295 * user owns on the launcher (or by other launchers, in case the user has multiple), use 296 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 297 * 298 * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other 299 * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this 300 * flag to get own pinned shortcuts. 301 */ 302 public static final int FLAG_MATCH_PINNED = 1 << 1; 303 304 /** @hide kept for unit tests */ 305 @Deprecated 306 public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED; 307 308 /** 309 * Include manifest shortcuts in the result. 310 */ 311 public static final int FLAG_MATCH_MANIFEST = 1 << 3; 312 313 /** @hide kept for unit tests */ 314 @Deprecated 315 public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; 316 317 /** 318 * Include all pinned shortcuts by any launchers, not just by the caller, 319 * in the result. 320 * 321 * <p>The caller must be the selected assistant app to use this flag, or have the system 322 * {@code ACCESS_SHORTCUTS} permission. 323 * 324 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 325 * user owns on the launcher (or by other launchers, in case the user has multiple), use 326 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 327 * 328 * <p>If you're a regular launcher app (or any app that's not the selected assistant app) 329 * then this flag will be ignored. 330 */ 331 public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10; 332 333 /** 334 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST 335 * @hide 336 */ 337 public static final int FLAG_MATCH_ALL_KINDS = 338 FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST; 339 340 /** 341 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED 342 * @hide 343 */ 344 public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED = 345 FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; 346 347 /** @hide kept for unit tests */ 348 @Deprecated 349 public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS; 350 351 /** 352 * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to 353 * see which fields fields "key". 354 * This allows quicker access to shortcut information in order to 355 * determine whether the caller's in-memory cache needs to be updated. 356 * 357 * <p>Typically, launcher applications cache all or most shortcut information 358 * in memory in order to show shortcuts without a delay. 359 * 360 * When a given launcher application wants to update its cache, such as when its process 361 * restarts, it can fetch shortcut information with this flag. 362 * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each 363 * shortcut, fetching a shortcut's non-key information only if that shortcut has been 364 * updated. 365 * 366 * @see ShortcutManager 367 */ 368 public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; 369 370 /** @hide */ 371 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 372 FLAG_MATCH_DYNAMIC, 373 FLAG_MATCH_PINNED, 374 FLAG_MATCH_MANIFEST, 375 FLAG_GET_KEY_FIELDS_ONLY, 376 FLAG_MATCH_MANIFEST, 377 }) 378 @Retention(RetentionPolicy.SOURCE) 379 public @interface QueryFlags {} 380 381 long mChangedSince; 382 383 @Nullable 384 String mPackage; 385 386 @Nullable 387 List<String> mShortcutIds; 388 389 @Nullable 390 ComponentName mActivity; 391 392 @QueryFlags 393 int mQueryFlags; 394 ShortcutQuery()395 public ShortcutQuery() { 396 } 397 398 /** 399 * If non-zero, returns only shortcuts that have been added or updated 400 * since the given timestamp, expressed in milliseconds since the Epoch—see 401 * {@link System#currentTimeMillis()}. 402 */ setChangedSince(long changedSince)403 public ShortcutQuery setChangedSince(long changedSince) { 404 mChangedSince = changedSince; 405 return this; 406 } 407 408 /** 409 * If non-null, returns only shortcuts from the package. 410 */ setPackage(@ullable String packageName)411 public ShortcutQuery setPackage(@Nullable String packageName) { 412 mPackage = packageName; 413 return this; 414 } 415 416 /** 417 * If non-null, return only the specified shortcuts by ID. When setting this field, 418 * a package name must also be set with {@link #setPackage}. 419 */ setShortcutIds(@ullable List<String> shortcutIds)420 public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) { 421 mShortcutIds = shortcutIds; 422 return this; 423 } 424 425 /** 426 * If non-null, returns only shortcuts associated with the activity; i.e. 427 * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal 428 * to {@code activity}. 429 */ setActivity(@ullable ComponentName activity)430 public ShortcutQuery setActivity(@Nullable ComponentName activity) { 431 mActivity = activity; 432 return this; 433 } 434 435 /** 436 * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, 437 * no shortcuts will be returned. 438 * 439 * <ul> 440 * <li>{@link #FLAG_MATCH_DYNAMIC} 441 * <li>{@link #FLAG_MATCH_PINNED} 442 * <li>{@link #FLAG_MATCH_MANIFEST} 443 * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY} 444 * </ul> 445 */ setQueryFlags(@ueryFlags int queryFlags)446 public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) { 447 mQueryFlags = queryFlags; 448 return this; 449 } 450 } 451 452 /** @hide */ LauncherApps(Context context, ILauncherApps service)453 public LauncherApps(Context context, ILauncherApps service) { 454 mContext = context; 455 mService = service; 456 mPm = context.getPackageManager(); 457 mUserManager = context.getSystemService(UserManager.class); 458 } 459 460 /** @hide */ 461 @TestApi LauncherApps(Context context)462 public LauncherApps(Context context) { 463 this(context, ILauncherApps.Stub.asInterface( 464 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE))); 465 } 466 467 /** 468 * Show an error log on logcat, when the calling user is a managed profile, and the target 469 * user is different from the calling user, in order to help developers to detect it. 470 */ logErrorForInvalidProfileAccess(@onNull UserHandle target)471 private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) { 472 if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) { 473 Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); 474 } 475 } 476 477 /** 478 * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs. 479 * 480 * <p>If the caller is running on a managed profile, it'll return only the current profile. 481 * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. 482 */ getProfiles()483 public List<UserHandle> getProfiles() { 484 if (mUserManager.isManagedProfile()) { 485 // If it's a managed profile, only return the current profile. 486 final List result = new ArrayList(1); 487 result.add(android.os.Process.myUserHandle()); 488 return result; 489 } else { 490 return mUserManager.getUserProfiles(); 491 } 492 } 493 494 /** 495 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and 496 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. 497 * 498 * @param packageName The specific package to query. If null, it checks all installed packages 499 * in the profile. 500 * @param user The UserHandle of the profile. 501 * @return List of launchable activities. Can be an empty list but will not be null. 502 */ getActivityList(String packageName, UserHandle user)503 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { 504 logErrorForInvalidProfileAccess(user); 505 try { 506 return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(), 507 packageName, user), user); 508 } catch (RemoteException re) { 509 throw re.rethrowFromSystemServer(); 510 } 511 } 512 513 /** 514 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it 515 * returns null. 516 * 517 * @param intent The intent to find a match for. 518 * @param user The profile to look in for a match. 519 * @return An activity info object if there is a match. 520 */ resolveActivity(Intent intent, UserHandle user)521 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) { 522 logErrorForInvalidProfileAccess(user); 523 try { 524 ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(), 525 intent.getComponent(), user); 526 if (ai != null) { 527 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user); 528 return info; 529 } 530 } catch (RemoteException re) { 531 throw re.rethrowFromSystemServer(); 532 } 533 return null; 534 } 535 536 /** 537 * Starts a Main activity in the specified profile. 538 * 539 * @param component The ComponentName of the activity to launch 540 * @param user The UserHandle of the profile 541 * @param sourceBounds The Rect containing the source bounds of the clicked icon 542 * @param opts Options to pass to startActivity 543 */ startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)544 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, 545 Bundle opts) { 546 logErrorForInvalidProfileAccess(user); 547 if (DEBUG) { 548 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier()); 549 } 550 try { 551 mService.startActivityAsUser(mContext.getIApplicationThread(), 552 mContext.getPackageName(), 553 component, sourceBounds, opts, user); 554 } catch (RemoteException re) { 555 throw re.rethrowFromSystemServer(); 556 } 557 } 558 559 /** 560 * Starts the settings activity to show the application details for a 561 * package in the specified profile. 562 * 563 * @param component The ComponentName of the package to launch settings for. 564 * @param user The UserHandle of the profile 565 * @param sourceBounds The Rect containing the source bounds of the clicked icon 566 * @param opts Options to pass to startActivity 567 */ startAppDetailsActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)568 public void startAppDetailsActivity(ComponentName component, UserHandle user, 569 Rect sourceBounds, Bundle opts) { 570 logErrorForInvalidProfileAccess(user); 571 try { 572 mService.showAppDetailsAsUser(mContext.getIApplicationThread(), 573 mContext.getPackageName(), 574 component, sourceBounds, opts, user); 575 } catch (RemoteException re) { 576 throw re.rethrowFromSystemServer(); 577 } 578 } 579 580 /** 581 * Retrieves a list of config activities for creating {@link ShortcutInfo}. 582 * 583 * @param packageName The specific package to query. If null, it checks all installed packages 584 * in the profile. 585 * @param user The UserHandle of the profile. 586 * @return List of config activities. Can be an empty list but will not be null. 587 * 588 * @see Intent#ACTION_CREATE_SHORTCUT 589 * @see #getShortcutConfigActivityIntent(LauncherActivityInfo) 590 */ getShortcutConfigActivityList(@ullable String packageName, @NonNull UserHandle user)591 public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName, 592 @NonNull UserHandle user) { 593 logErrorForInvalidProfileAccess(user); 594 try { 595 return convertToActivityList(mService.getShortcutConfigActivities( 596 mContext.getPackageName(), packageName, user), 597 user); 598 } catch (RemoteException re) { 599 throw re.rethrowFromSystemServer(); 600 } 601 } 602 convertToActivityList( @ullable ParceledListSlice<ResolveInfo> activities, UserHandle user)603 private List<LauncherActivityInfo> convertToActivityList( 604 @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) { 605 if (activities == null) { 606 return Collections.EMPTY_LIST; 607 } 608 ArrayList<LauncherActivityInfo> lais = new ArrayList<>(); 609 for (ResolveInfo ri : activities.getList()) { 610 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user); 611 if (DEBUG) { 612 Log.v(TAG, "Returning activity for profile " + user + " : " 613 + lai.getComponentName()); 614 } 615 lais.add(lai); 616 } 617 return lais; 618 } 619 620 /** 621 * Returns an intent sender which can be used to start the configure activity for creating 622 * custom shortcuts. Use this method if the provider is in another profile as you are not 623 * allowed to start an activity in another profile. 624 * 625 * <p>The caller should receive {@link PinItemRequest} in onActivityResult on 626 * {@link android.app.Activity#RESULT_OK}. 627 * 628 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 629 * #hasShortcutHostPermission()}. 630 * 631 * @param info a configuration activity returned by {@link #getShortcutConfigActivityList} 632 * 633 * @throws IllegalStateException when the user is locked or not running. 634 * @throws SecurityException if {@link #hasShortcutHostPermission()} is false. 635 * 636 * @see #getPinItemRequest(Intent) 637 * @see Intent#ACTION_CREATE_SHORTCUT 638 * @see android.app.Activity#startIntentSenderForResult 639 */ 640 @Nullable getShortcutConfigActivityIntent(@onNull LauncherActivityInfo info)641 public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) { 642 try { 643 return mService.getShortcutConfigActivityIntent( 644 mContext.getPackageName(), info.getComponentName(), info.getUser()); 645 } catch (RemoteException re) { 646 throw re.rethrowFromSystemServer(); 647 } 648 } 649 650 /** 651 * Checks if the package is installed and enabled for a profile. 652 * 653 * @param packageName The package to check. 654 * @param user The UserHandle of the profile. 655 * 656 * @return true if the package exists and is enabled. 657 */ isPackageEnabled(String packageName, UserHandle user)658 public boolean isPackageEnabled(String packageName, UserHandle user) { 659 logErrorForInvalidProfileAccess(user); 660 try { 661 return mService.isPackageEnabled(mContext.getPackageName(), packageName, user); 662 } catch (RemoteException re) { 663 throw re.rethrowFromSystemServer(); 664 } 665 } 666 667 /** 668 * Gets the launcher extras supplied to the system when the given package was suspended via 669 * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, 670 * PersistableBundle, String)}. 671 * 672 * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending 673 * app and the launcher. 674 * 675 * <p>Note: This just returns whatever extras were provided to the system, <em>which might 676 * even be {@code null}.</em> 677 * 678 * @param packageName The package for which to fetch the launcher extras. 679 * @param user The {@link UserHandle} of the profile. 680 * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently 681 * suspended. 682 * 683 * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle) 684 * @see PackageManager#isPackageSuspended() 685 */ getSuspendedPackageLauncherExtras(String packageName, UserHandle user)686 public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) { 687 logErrorForInvalidProfileAccess(user); 688 try { 689 return mService.getSuspendedPackageLauncherExtras(packageName, user); 690 } catch (RemoteException re) { 691 throw re.rethrowFromSystemServer(); 692 } 693 } 694 695 /** 696 * Returns {@link ApplicationInfo} about an application installed for a specific user profile. 697 * 698 * @param packageName The package name of the application 699 * @param flags Additional option flags {@link PackageManager#getApplicationInfo} 700 * @param user The UserHandle of the profile. 701 * 702 * @return {@link ApplicationInfo} containing information about the package. Returns 703 * {@code null} if the package isn't installed for the given profile, or the profile 704 * isn't enabled. 705 */ getApplicationInfo(@onNull String packageName, @ApplicationInfoFlags int flags, @NonNull UserHandle user)706 public ApplicationInfo getApplicationInfo(@NonNull String packageName, 707 @ApplicationInfoFlags int flags, @NonNull UserHandle user) 708 throws PackageManager.NameNotFoundException { 709 Preconditions.checkNotNull(packageName, "packageName"); 710 Preconditions.checkNotNull(user, "user"); 711 logErrorForInvalidProfileAccess(user); 712 try { 713 final ApplicationInfo ai = mService 714 .getApplicationInfo(mContext.getPackageName(), packageName, flags, user); 715 if (ai == null) { 716 throw new NameNotFoundException("Package " + packageName + " not found for user " 717 + user.getIdentifier()); 718 } 719 return ai; 720 } catch (RemoteException re) { 721 throw re.rethrowFromSystemServer(); 722 } 723 } 724 725 /** 726 * Checks if the activity exists and it enabled for a profile. 727 * 728 * @param component The activity to check. 729 * @param user The UserHandle of the profile. 730 * 731 * @return true if the activity exists and is enabled. 732 */ isActivityEnabled(ComponentName component, UserHandle user)733 public boolean isActivityEnabled(ComponentName component, UserHandle user) { 734 logErrorForInvalidProfileAccess(user); 735 try { 736 return mService.isActivityEnabled(mContext.getPackageName(), component, user); 737 } catch (RemoteException re) { 738 throw re.rethrowFromSystemServer(); 739 } 740 } 741 742 /** 743 * Returns whether the caller can access the shortcut information. Access is currently 744 * available to: 745 * 746 * <ul> 747 * <li>The current launcher (or default launcher if there is no set current launcher).</li> 748 * <li>The currently active voice interaction service.</li> 749 * </ul> 750 * 751 * <p>Note when this method returns {@code false}, it may be a temporary situation because 752 * the user is trying a new launcher application. The user may decide to change the default 753 * launcher back to the calling application again, so even if a launcher application loses 754 * this permission, it does <b>not</b> have to purge pinned shortcut information. 755 * If the calling launcher application contains pinned shortcuts, they will still work, 756 * even though the caller no longer has the shortcut host permission. 757 * 758 * @throws IllegalStateException when the user is locked. 759 * 760 * @see ShortcutManager 761 */ hasShortcutHostPermission()762 public boolean hasShortcutHostPermission() { 763 try { 764 return mService.hasShortcutHostPermission(mContext.getPackageName()); 765 } catch (RemoteException re) { 766 throw re.rethrowFromSystemServer(); 767 } 768 } 769 maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts)770 private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) { 771 if (shortcuts == null) { 772 return null; 773 } 774 for (int i = shortcuts.size() - 1; i >= 0; i--) { 775 final ShortcutInfo si = shortcuts.get(i); 776 final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext, 777 si.getDisabledReason()); 778 if (message != null) { 779 si.setDisabledMessage(message); 780 } 781 } 782 return shortcuts; 783 } 784 785 /** 786 * Returns {@link ShortcutInfo}s that match {@code query}. 787 * 788 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 789 * #hasShortcutHostPermission()}. 790 * 791 * @param query result includes shortcuts matching this query. 792 * @param user The UserHandle of the profile. 793 * 794 * @return the IDs of {@link ShortcutInfo}s that match the query. 795 * @throws IllegalStateException when the user is locked, or when the {@code user} user 796 * is locked or not running. 797 * 798 * @see ShortcutManager 799 */ 800 @Nullable getShortcuts(@onNull ShortcutQuery query, @NonNull UserHandle user)801 public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query, 802 @NonNull UserHandle user) { 803 logErrorForInvalidProfileAccess(user); 804 try { 805 // Note this is the only case we need to update the disabled message for shortcuts 806 // that weren't restored. 807 // The restore problem messages are only shown by the user, and publishers will never 808 // see them. The only other API that the launcher gets shortcuts is the shortcut 809 // changed callback, but that only returns shortcuts with the "key" information, so 810 // that won't return disabled message. 811 return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(), 812 query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, 813 query.mQueryFlags, user) 814 .getList()); 815 } catch (RemoteException e) { 816 throw e.rethrowFromSystemServer(); 817 } 818 } 819 820 /** 821 * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. 822 */ 823 @Nullable 824 @Deprecated getShortcutInfo(@onNull String packageName, @NonNull List<String> ids, @NonNull UserHandle user)825 public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName, 826 @NonNull List<String> ids, @NonNull UserHandle user) { 827 final ShortcutQuery q = new ShortcutQuery(); 828 q.setPackage(packageName); 829 q.setShortcutIds(ids); 830 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 831 return getShortcuts(q, user); 832 } 833 834 /** 835 * Pin shortcuts on a package. 836 * 837 * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package. 838 * However, different launchers may have different set of pinned shortcuts. 839 * 840 * <p>The calling launcher application must be allowed to access the shortcut information, 841 * as defined in {@link #hasShortcutHostPermission()}. 842 * 843 * @param packageName The target package name. 844 * @param shortcutIds The IDs of the shortcut to be pinned. 845 * @param user The UserHandle of the profile. 846 * @throws IllegalStateException when the user is locked, or when the {@code user} user 847 * is locked or not running. 848 * 849 * @see ShortcutManager 850 */ pinShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user)851 public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 852 @NonNull UserHandle user) { 853 logErrorForInvalidProfileAccess(user); 854 try { 855 mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); 856 } catch (RemoteException e) { 857 throw e.rethrowFromSystemServer(); 858 } 859 } 860 861 /** 862 * @hide kept for testing. 863 */ 864 @Deprecated getShortcutIconResId(@onNull ShortcutInfo shortcut)865 public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { 866 return shortcut.getIconResourceId(); 867 } 868 869 /** 870 * @hide kept for testing. 871 */ 872 @Deprecated getShortcutIconResId(@onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)873 public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, 874 @NonNull UserHandle user) { 875 final ShortcutQuery q = new ShortcutQuery(); 876 q.setPackage(packageName); 877 q.setShortcutIds(Arrays.asList(shortcutId)); 878 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 879 final List<ShortcutInfo> shortcuts = getShortcuts(q, user); 880 881 return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0; 882 } 883 884 /** 885 * @hide internal/unit tests only 886 */ getShortcutIconFd( @onNull ShortcutInfo shortcut)887 public ParcelFileDescriptor getShortcutIconFd( 888 @NonNull ShortcutInfo shortcut) { 889 return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(), 890 shortcut.getUserId()); 891 } 892 893 /** 894 * @hide internal/unit tests only 895 */ getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)896 public ParcelFileDescriptor getShortcutIconFd( 897 @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { 898 return getShortcutIconFd(packageName, shortcutId, user.getIdentifier()); 899 } 900 getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, int userId)901 private ParcelFileDescriptor getShortcutIconFd( 902 @NonNull String packageName, @NonNull String shortcutId, int userId) { 903 try { 904 return mService.getShortcutIconFd(mContext.getPackageName(), 905 packageName, shortcutId, userId); 906 } catch (RemoteException e) { 907 throw e.rethrowFromSystemServer(); 908 } 909 } 910 911 /** 912 * Returns the icon for this shortcut, without any badging for the profile. 913 * 914 * <p>The calling launcher application must be allowed to access the shortcut information, 915 * as defined in {@link #hasShortcutHostPermission()}. 916 * 917 * @param density The preferred density of the icon, zero for default density. Use 918 * density DPI values from {@link DisplayMetrics}. 919 * 920 * @return The drawable associated with the shortcut. 921 * @throws IllegalStateException when the user is locked, or when the {@code user} user 922 * is locked or not running. 923 * 924 * @see ShortcutManager 925 * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) 926 * @see DisplayMetrics 927 */ getShortcutIconDrawable(@onNull ShortcutInfo shortcut, int density)928 public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { 929 if (shortcut.hasIconFile()) { 930 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); 931 if (pfd == null) { 932 return null; 933 } 934 try { 935 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); 936 if (bmp != null) { 937 BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp); 938 if (shortcut.hasAdaptiveBitmap()) { 939 return new AdaptiveIconDrawable(null, dr); 940 } else { 941 return dr; 942 } 943 } 944 return null; 945 } finally { 946 try { 947 pfd.close(); 948 } catch (IOException ignore) { 949 } 950 } 951 } else if (shortcut.hasIconResource()) { 952 return loadDrawableResourceFromPackage(shortcut.getPackage(), 953 shortcut.getIconResourceId(), shortcut.getUserHandle(), density); 954 } else if (shortcut.getIcon() != null) { 955 // This happens if a shortcut is pending-approval. 956 final Icon icon = shortcut.getIcon(); 957 switch (icon.getType()) { 958 case Icon.TYPE_RESOURCE: { 959 return loadDrawableResourceFromPackage(shortcut.getPackage(), 960 icon.getResId(), shortcut.getUserHandle(), density); 961 } 962 case Icon.TYPE_BITMAP: 963 case Icon.TYPE_ADAPTIVE_BITMAP: { 964 return icon.loadDrawable(mContext); 965 } 966 default: 967 return null; // Shouldn't happen though. 968 } 969 } else { 970 return null; // Has no icon. 971 } 972 } 973 loadDrawableResourceFromPackage(String packageName, int resId, UserHandle user, int density)974 private Drawable loadDrawableResourceFromPackage(String packageName, int resId, 975 UserHandle user, int density) { 976 try { 977 if (resId == 0) { 978 return null; // Shouldn't happen but just in case. 979 } 980 final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user); 981 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai); 982 return res.getDrawableForDensity(resId, density); 983 } catch (NameNotFoundException | Resources.NotFoundException e) { 984 return null; 985 } 986 } 987 988 /** 989 * Returns the shortcut icon with badging appropriate for the profile. 990 * 991 * <p>The calling launcher application must be allowed to access the shortcut information, 992 * as defined in {@link #hasShortcutHostPermission()}. 993 * 994 * @param density Optional density for the icon, or 0 to use the default density. Use 995 * @return A badged icon for the shortcut. 996 * @throws IllegalStateException when the user is locked, or when the {@code user} user 997 * is locked or not running. 998 * 999 * @see ShortcutManager 1000 * @see #getShortcutIconDrawable(ShortcutInfo, int) 1001 * @see DisplayMetrics 1002 */ getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density)1003 public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) { 1004 final Drawable originalIcon = getShortcutIconDrawable(shortcut, density); 1005 1006 return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon( 1007 originalIcon, shortcut.getUserHandle()); 1008 } 1009 1010 /** 1011 * Starts a shortcut. 1012 * 1013 * <p>The calling launcher application must be allowed to access the shortcut information, 1014 * as defined in {@link #hasShortcutHostPermission()}. 1015 * 1016 * @param packageName The target shortcut package name. 1017 * @param shortcutId The target shortcut ID. 1018 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1019 * @param startActivityOptions Options to pass to startActivity. 1020 * @param user The UserHandle of the profile. 1021 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1022 * is locked or not running. 1023 * 1024 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1025 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1026 */ startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user)1027 public void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1028 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1029 @NonNull UserHandle user) { 1030 logErrorForInvalidProfileAccess(user); 1031 1032 startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, 1033 user.getIdentifier()); 1034 } 1035 1036 /** 1037 * Launches a shortcut. 1038 * 1039 * <p>The calling launcher application must be allowed to access the shortcut information, 1040 * as defined in {@link #hasShortcutHostPermission()}. 1041 * 1042 * @param shortcut The target shortcut. 1043 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1044 * @param startActivityOptions Options to pass to startActivity. 1045 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1046 * is locked or not running. 1047 * 1048 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1049 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1050 */ startShortcut(@onNull ShortcutInfo shortcut, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions)1051 public void startShortcut(@NonNull ShortcutInfo shortcut, 1052 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { 1053 startShortcut(shortcut.getPackage(), shortcut.getId(), 1054 sourceBounds, startActivityOptions, 1055 shortcut.getUserId()); 1056 } 1057 startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, int userId)1058 private void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1059 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1060 int userId) { 1061 try { 1062 final boolean success = 1063 mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, 1064 sourceBounds, startActivityOptions, userId); 1065 if (!success) { 1066 throw new ActivityNotFoundException("Shortcut could not be started"); 1067 } 1068 } catch (RemoteException e) { 1069 throw e.rethrowFromSystemServer(); 1070 } 1071 } 1072 1073 /** 1074 * Registers a callback for changes to packages in current and managed profiles. 1075 * 1076 * @param callback The callback to register. 1077 */ registerCallback(Callback callback)1078 public void registerCallback(Callback callback) { 1079 registerCallback(callback, null); 1080 } 1081 1082 /** 1083 * Registers a callback for changes to packages in current and managed profiles. 1084 * 1085 * @param callback The callback to register. 1086 * @param handler that should be used to post callbacks on, may be null. 1087 */ registerCallback(Callback callback, Handler handler)1088 public void registerCallback(Callback callback, Handler handler) { 1089 synchronized (this) { 1090 if (callback != null && findCallbackLocked(callback) < 0) { 1091 boolean addedFirstCallback = mCallbacks.size() == 0; 1092 addCallbackLocked(callback, handler); 1093 if (addedFirstCallback) { 1094 try { 1095 mService.addOnAppsChangedListener(mContext.getPackageName(), 1096 mAppsChangedListener); 1097 } catch (RemoteException re) { 1098 throw re.rethrowFromSystemServer(); 1099 } 1100 } 1101 } 1102 } 1103 } 1104 1105 /** 1106 * Unregisters a callback that was previously registered. 1107 * 1108 * @param callback The callback to unregister. 1109 * @see #registerCallback(Callback) 1110 */ unregisterCallback(Callback callback)1111 public void unregisterCallback(Callback callback) { 1112 synchronized (this) { 1113 removeCallbackLocked(callback); 1114 if (mCallbacks.size() == 0) { 1115 try { 1116 mService.removeOnAppsChangedListener(mAppsChangedListener); 1117 } catch (RemoteException re) { 1118 throw re.rethrowFromSystemServer(); 1119 } 1120 } 1121 } 1122 } 1123 1124 /** @return position in mCallbacks for callback or -1 if not present. */ findCallbackLocked(Callback callback)1125 private int findCallbackLocked(Callback callback) { 1126 if (callback == null) { 1127 throw new IllegalArgumentException("Callback cannot be null"); 1128 } 1129 final int size = mCallbacks.size(); 1130 for (int i = 0; i < size; ++i) { 1131 if (mCallbacks.get(i).mCallback == callback) { 1132 return i; 1133 } 1134 } 1135 return -1; 1136 } 1137 removeCallbackLocked(Callback callback)1138 private void removeCallbackLocked(Callback callback) { 1139 int pos = findCallbackLocked(callback); 1140 if (pos >= 0) { 1141 mCallbacks.remove(pos); 1142 } 1143 } 1144 addCallbackLocked(Callback callback, Handler handler)1145 private void addCallbackLocked(Callback callback, Handler handler) { 1146 // Remove if already present. 1147 removeCallbackLocked(callback); 1148 if (handler == null) { 1149 handler = new Handler(); 1150 } 1151 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback); 1152 mCallbacks.add(toAdd); 1153 } 1154 1155 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() { 1156 1157 @Override 1158 public void onPackageRemoved(UserHandle user, String packageName) 1159 throws RemoteException { 1160 if (DEBUG) { 1161 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName); 1162 } 1163 synchronized (LauncherApps.this) { 1164 for (CallbackMessageHandler callback : mCallbacks) { 1165 callback.postOnPackageRemoved(packageName, user); 1166 } 1167 } 1168 } 1169 1170 @Override 1171 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException { 1172 if (DEBUG) { 1173 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName); 1174 } 1175 synchronized (LauncherApps.this) { 1176 for (CallbackMessageHandler callback : mCallbacks) { 1177 callback.postOnPackageChanged(packageName, user); 1178 } 1179 } 1180 } 1181 1182 @Override 1183 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException { 1184 if (DEBUG) { 1185 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName); 1186 } 1187 synchronized (LauncherApps.this) { 1188 for (CallbackMessageHandler callback : mCallbacks) { 1189 callback.postOnPackageAdded(packageName, user); 1190 } 1191 } 1192 } 1193 1194 @Override 1195 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) 1196 throws RemoteException { 1197 if (DEBUG) { 1198 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames); 1199 } 1200 synchronized (LauncherApps.this) { 1201 for (CallbackMessageHandler callback : mCallbacks) { 1202 callback.postOnPackagesAvailable(packageNames, user, replacing); 1203 } 1204 } 1205 } 1206 1207 @Override 1208 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing) 1209 throws RemoteException { 1210 if (DEBUG) { 1211 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames); 1212 } 1213 synchronized (LauncherApps.this) { 1214 for (CallbackMessageHandler callback : mCallbacks) { 1215 callback.postOnPackagesUnavailable(packageNames, user, replacing); 1216 } 1217 } 1218 } 1219 1220 @Override 1221 public void onPackagesSuspended(UserHandle user, String[] packageNames, 1222 Bundle launcherExtras) 1223 throws RemoteException { 1224 if (DEBUG) { 1225 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames); 1226 } 1227 synchronized (LauncherApps.this) { 1228 for (CallbackMessageHandler callback : mCallbacks) { 1229 callback.postOnPackagesSuspended(packageNames, launcherExtras, user); 1230 } 1231 } 1232 } 1233 1234 @Override 1235 public void onPackagesUnsuspended(UserHandle user, String[] packageNames) 1236 throws RemoteException { 1237 if (DEBUG) { 1238 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames); 1239 } 1240 synchronized (LauncherApps.this) { 1241 for (CallbackMessageHandler callback : mCallbacks) { 1242 callback.postOnPackagesUnsuspended(packageNames, user); 1243 } 1244 } 1245 } 1246 1247 @Override 1248 public void onShortcutChanged(UserHandle user, String packageName, 1249 ParceledListSlice shortcuts) { 1250 if (DEBUG) { 1251 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName); 1252 } 1253 final List<ShortcutInfo> list = shortcuts.getList(); 1254 synchronized (LauncherApps.this) { 1255 for (CallbackMessageHandler callback : mCallbacks) { 1256 callback.postOnShortcutChanged(packageName, user, list); 1257 } 1258 } 1259 } 1260 }; 1261 1262 private static class CallbackMessageHandler extends Handler { 1263 private static final int MSG_ADDED = 1; 1264 private static final int MSG_REMOVED = 2; 1265 private static final int MSG_CHANGED = 3; 1266 private static final int MSG_AVAILABLE = 4; 1267 private static final int MSG_UNAVAILABLE = 5; 1268 private static final int MSG_SUSPENDED = 6; 1269 private static final int MSG_UNSUSPENDED = 7; 1270 private static final int MSG_SHORTCUT_CHANGED = 8; 1271 1272 private LauncherApps.Callback mCallback; 1273 1274 private static class CallbackInfo { 1275 String[] packageNames; 1276 String packageName; 1277 Bundle launcherExtras; 1278 boolean replacing; 1279 UserHandle user; 1280 List<ShortcutInfo> shortcuts; 1281 } 1282 CallbackMessageHandler(Looper looper, LauncherApps.Callback callback)1283 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) { 1284 super(looper, null, true); 1285 mCallback = callback; 1286 } 1287 1288 @Override handleMessage(Message msg)1289 public void handleMessage(Message msg) { 1290 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) { 1291 return; 1292 } 1293 CallbackInfo info = (CallbackInfo) msg.obj; 1294 switch (msg.what) { 1295 case MSG_ADDED: 1296 mCallback.onPackageAdded(info.packageName, info.user); 1297 break; 1298 case MSG_REMOVED: 1299 mCallback.onPackageRemoved(info.packageName, info.user); 1300 break; 1301 case MSG_CHANGED: 1302 mCallback.onPackageChanged(info.packageName, info.user); 1303 break; 1304 case MSG_AVAILABLE: 1305 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing); 1306 break; 1307 case MSG_UNAVAILABLE: 1308 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing); 1309 break; 1310 case MSG_SUSPENDED: 1311 mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras 1312 ); 1313 break; 1314 case MSG_UNSUSPENDED: 1315 mCallback.onPackagesUnsuspended(info.packageNames, info.user); 1316 break; 1317 case MSG_SHORTCUT_CHANGED: 1318 mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user); 1319 break; 1320 } 1321 } 1322 postOnPackageAdded(String packageName, UserHandle user)1323 public void postOnPackageAdded(String packageName, UserHandle user) { 1324 CallbackInfo info = new CallbackInfo(); 1325 info.packageName = packageName; 1326 info.user = user; 1327 obtainMessage(MSG_ADDED, info).sendToTarget(); 1328 } 1329 postOnPackageRemoved(String packageName, UserHandle user)1330 public void postOnPackageRemoved(String packageName, UserHandle user) { 1331 CallbackInfo info = new CallbackInfo(); 1332 info.packageName = packageName; 1333 info.user = user; 1334 obtainMessage(MSG_REMOVED, info).sendToTarget(); 1335 } 1336 postOnPackageChanged(String packageName, UserHandle user)1337 public void postOnPackageChanged(String packageName, UserHandle user) { 1338 CallbackInfo info = new CallbackInfo(); 1339 info.packageName = packageName; 1340 info.user = user; 1341 obtainMessage(MSG_CHANGED, info).sendToTarget(); 1342 } 1343 postOnPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)1344 public void postOnPackagesAvailable(String[] packageNames, UserHandle user, 1345 boolean replacing) { 1346 CallbackInfo info = new CallbackInfo(); 1347 info.packageNames = packageNames; 1348 info.replacing = replacing; 1349 info.user = user; 1350 obtainMessage(MSG_AVAILABLE, info).sendToTarget(); 1351 } 1352 postOnPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)1353 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user, 1354 boolean replacing) { 1355 CallbackInfo info = new CallbackInfo(); 1356 info.packageNames = packageNames; 1357 info.replacing = replacing; 1358 info.user = user; 1359 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); 1360 } 1361 postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, UserHandle user)1362 public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, 1363 UserHandle user) { 1364 CallbackInfo info = new CallbackInfo(); 1365 info.packageNames = packageNames; 1366 info.user = user; 1367 info.launcherExtras = launcherExtras; 1368 obtainMessage(MSG_SUSPENDED, info).sendToTarget(); 1369 } 1370 postOnPackagesUnsuspended(String[] packageNames, UserHandle user)1371 public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) { 1372 CallbackInfo info = new CallbackInfo(); 1373 info.packageNames = packageNames; 1374 info.user = user; 1375 obtainMessage(MSG_UNSUSPENDED, info).sendToTarget(); 1376 } 1377 postOnShortcutChanged(String packageName, UserHandle user, List<ShortcutInfo> shortcuts)1378 public void postOnShortcutChanged(String packageName, UserHandle user, 1379 List<ShortcutInfo> shortcuts) { 1380 CallbackInfo info = new CallbackInfo(); 1381 info.packageName = packageName; 1382 info.user = user; 1383 info.shortcuts = shortcuts; 1384 obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget(); 1385 } 1386 } 1387 1388 /** 1389 * A helper method to extract a {@link PinItemRequest} set to 1390 * the {@link #EXTRA_PIN_ITEM_REQUEST} extra. 1391 */ getPinItemRequest(Intent intent)1392 public PinItemRequest getPinItemRequest(Intent intent) { 1393 return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST); 1394 } 1395 1396 /** 1397 * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with 1398 * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent 1399 * respectively to the default launcher app. 1400 * 1401 * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type. 1402 * 1403 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1404 * {@link ShortcutInfo}. If the launcher accepts a request, call {@link #accept()}, 1405 * or {@link #accept(Bundle)} with a null or empty Bundle. No options are defined for 1406 * pin-shortcuts requests. 1407 * 1408 * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type. 1409 * 1410 * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in 1411 * which case {@link ShortcutInfo#isPinned()} returns true. This means the user wants to create 1412 * another pinned shortcut for a shortcut that's already pinned. If the launcher accepts it, 1413 * {@link #accept()} must still be called even though the shortcut is already pinned, and 1414 * create a new pinned shortcut icon for it. 1415 * 1416 * <p>See also {@link ShortcutManager} for more details. 1417 * 1418 * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type. 1419 * 1420 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1421 * an AppWidget. If the launcher accepts a request, call {@link #accept(Bundle)} with 1422 * the appwidget integer ID set to the 1423 * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra. 1424 * 1425 * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null 1426 * {@link AppWidgetProviderInfo} for this type. 1427 * 1428 * <p>See also {@link AppWidgetManager} for more details. 1429 * 1430 * @see #EXTRA_PIN_ITEM_REQUEST 1431 * @see #getPinItemRequest(Intent) 1432 */ 1433 public static final class PinItemRequest implements Parcelable { 1434 1435 /** This is a request to pin shortcut. */ 1436 public static final int REQUEST_TYPE_SHORTCUT = 1; 1437 1438 /** This is a request to pin app widget. */ 1439 public static final int REQUEST_TYPE_APPWIDGET = 2; 1440 1441 /** @hide */ 1442 @IntDef(prefix = { "REQUEST_TYPE_" }, value = { 1443 REQUEST_TYPE_SHORTCUT, 1444 REQUEST_TYPE_APPWIDGET 1445 }) 1446 @Retention(RetentionPolicy.SOURCE) 1447 public @interface RequestType {} 1448 1449 private final int mRequestType; 1450 private final IPinItemRequest mInner; 1451 1452 /** 1453 * @hide 1454 */ PinItemRequest(IPinItemRequest inner, int type)1455 public PinItemRequest(IPinItemRequest inner, int type) { 1456 mInner = inner; 1457 mRequestType = type; 1458 } 1459 1460 /** 1461 * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants. 1462 * 1463 * @return one of the {@code REQUEST_TYPE_} constants. 1464 */ 1465 @RequestType getRequestType()1466 public int getRequestType() { 1467 return mRequestType; 1468 } 1469 1470 /** 1471 * {@link ShortcutInfo} sent by the requesting app. 1472 * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a 1473 * different request type. 1474 * 1475 * @return requested {@link ShortcutInfo} when a request is of the 1476 * {@link #REQUEST_TYPE_SHORTCUT} type. Null otherwise. 1477 */ 1478 @Nullable getShortcutInfo()1479 public ShortcutInfo getShortcutInfo() { 1480 try { 1481 return mInner.getShortcutInfo(); 1482 } catch (RemoteException e) { 1483 throw e.rethrowAsRuntimeException(); 1484 } 1485 } 1486 1487 /** 1488 * {@link AppWidgetProviderInfo} sent by the requesting app. 1489 * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a 1490 * different request type. 1491 * 1492 * <p>Launcher should not show any configuration activity associated with the provider, and 1493 * assume that the widget is already fully configured. Upon accepting the widget, it should 1494 * pass the widgetId in {@link #accept(Bundle)}. 1495 * 1496 * @return requested {@link AppWidgetProviderInfo} when a request is of the 1497 * {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise. 1498 */ 1499 @Nullable getAppWidgetProviderInfo(Context context)1500 public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) { 1501 try { 1502 final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo(); 1503 if (info == null) { 1504 return null; 1505 } 1506 info.updateDimensions(context.getResources().getDisplayMetrics()); 1507 return info; 1508 } catch (RemoteException e) { 1509 throw e.rethrowAsRuntimeException(); 1510 } 1511 } 1512 1513 /** 1514 * Any extras sent by the requesting app. 1515 * 1516 * @return For a shortcut request, this method always return null. For an AppWidget 1517 * request, this method returns the extras passed to the 1518 * {@link android.appwidget.AppWidgetManager#requestPinAppWidget( 1519 * ComponentName, Bundle, PendingIntent)} API. See {@link AppWidgetManager} for details. 1520 */ 1521 @Nullable getExtras()1522 public Bundle getExtras() { 1523 try { 1524 return mInner.getExtras(); 1525 } catch (RemoteException e) { 1526 throw e.rethrowAsRuntimeException(); 1527 } 1528 } 1529 1530 /** 1531 * Return whether a request is still valid. 1532 * 1533 * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called. 1534 */ isValid()1535 public boolean isValid() { 1536 try { 1537 return mInner.isValid(); 1538 } catch (RemoteException e) { 1539 return false; 1540 } 1541 } 1542 1543 /** 1544 * Called by the receiving launcher app when the user accepts the request. 1545 * 1546 * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request. 1547 * 1548 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 1549 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 1550 * already been canceled, in which case the launcher must not pin the requested item. 1551 */ accept(@ullable Bundle options)1552 public boolean accept(@Nullable Bundle options) { 1553 try { 1554 return mInner.accept(options); 1555 } catch (RemoteException e) { 1556 throw e.rethrowFromSystemServer(); 1557 } 1558 } 1559 1560 /** 1561 * Called by the receiving launcher app when the user accepts the request, with no options. 1562 * 1563 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 1564 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 1565 * already been canceled, in which case the launcher must not pin the requested item. 1566 */ accept()1567 public boolean accept() { 1568 return accept(/* options= */ null); 1569 } 1570 PinItemRequest(Parcel source)1571 private PinItemRequest(Parcel source) { 1572 final ClassLoader cl = getClass().getClassLoader(); 1573 1574 mRequestType = source.readInt(); 1575 mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder()); 1576 } 1577 1578 @Override writeToParcel(Parcel dest, int flags)1579 public void writeToParcel(Parcel dest, int flags) { 1580 dest.writeInt(mRequestType); 1581 dest.writeStrongBinder(mInner.asBinder()); 1582 } 1583 1584 public static final Creator<PinItemRequest> CREATOR = 1585 new Creator<PinItemRequest>() { 1586 public PinItemRequest createFromParcel(Parcel source) { 1587 return new PinItemRequest(source); 1588 } 1589 public PinItemRequest[] newArray(int size) { 1590 return new PinItemRequest[size]; 1591 } 1592 }; 1593 1594 @Override describeContents()1595 public int describeContents() { 1596 return 0; 1597 } 1598 } 1599 } 1600