Home | History | Annotate | Download | only in pm
      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} &amp; {@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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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 
    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&mdash;see
    401          * {@link System#currentTimeMillis()}.
    402          */
    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          */
    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          */
    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          */
    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          */
    446         public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
    447             mQueryFlags = queryFlags;
    448             return this;
    449         }
    450     }
    451 
    452     /** @hide */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    762     public boolean hasShortcutHostPermission() {
    763         try {
    764             return mService.hasShortcutHostPermission(mContext.getPackageName());
    765         } catch (RemoteException re) {
    766             throw re.rethrowFromSystemServer();
    767         }
    768     }
    769 
    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
    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
    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      */
    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
    865     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
    866         return shortcut.getIconResourceId();
    867     }
    868 
    869     /**
    870      * @hide kept for testing.
    871      */
    872     @Deprecated
    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      */
    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      */
    896     public ParcelFileDescriptor getShortcutIconFd(
    897             @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
    898         return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
    899     }
    900 
    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      */
    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 
    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      */
   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      */
   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      */
   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 
   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      */
   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      */
   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      */
   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. */
   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 
   1138     private void removeCallbackLocked(Callback callback) {
   1139         int pos = findCallbackLocked(callback);
   1140         if (pos >= 0) {
   1141             mCallbacks.remove(pos);
   1142         }
   1143     }
   1144 
   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 
   1283         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
   1284             super(looper, null, true);
   1285             mCallback = callback;
   1286         }
   1287 
   1288         @Override
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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      */
   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          */
   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
   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
   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
   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
   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          */
   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          */
   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          */
   1567         public boolean accept() {
   1568             return accept(/* options= */ null);
   1569         }
   1570 
   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
   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
   1595         public int describeContents() {
   1596             return 0;
   1597         }
   1598     }
   1599 }
   1600