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