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.TestApi;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.pm.PackageManager.ApplicationInfoFlags;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.content.res.Resources;
     30 import android.graphics.Bitmap;
     31 import android.graphics.BitmapFactory;
     32 import android.graphics.Rect;
     33 import android.graphics.drawable.BitmapDrawable;
     34 import android.graphics.drawable.Drawable;
     35 import android.os.Bundle;
     36 import android.os.Handler;
     37 import android.os.Looper;
     38 import android.os.Message;
     39 import android.os.ParcelFileDescriptor;
     40 import android.os.RemoteException;
     41 import android.os.ServiceManager;
     42 import android.os.UserHandle;
     43 import android.os.UserManager;
     44 import android.util.DisplayMetrics;
     45 import android.util.Log;
     46 
     47 import java.io.IOException;
     48 import java.lang.annotation.Retention;
     49 import java.lang.annotation.RetentionPolicy;
     50 import java.util.ArrayList;
     51 import java.util.Arrays;
     52 import java.util.Collections;
     53 import java.util.List;
     54 
     55 /**
     56  * Class for retrieving a list of launchable activities for the current user and any associated
     57  * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
     58  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
     59  * for package changes here.
     60  * <p>
     61  * To watch for managed profiles being added or removed, register for the following broadcasts:
     62  * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
     63  * <p>
     64  * You can retrieve the list of profiles associated with this user with
     65  * {@link UserManager#getUserProfiles()}.
     66  */
     67 public class LauncherApps {
     68 
     69     static final String TAG = "LauncherApps";
     70     static final boolean DEBUG = false;
     71 
     72     private Context mContext;
     73     private ILauncherApps mService;
     74     private PackageManager mPm;
     75 
     76     private List<CallbackMessageHandler> mCallbacks
     77             = new ArrayList<CallbackMessageHandler>();
     78 
     79     /**
     80      * Callbacks for package changes to this and related managed profiles.
     81      */
     82     public static abstract class Callback {
     83         /**
     84          * Indicates that a package was removed from the specified profile.
     85          *
     86          * If a package is removed while being updated onPackageChanged will be
     87          * called instead.
     88          *
     89          * @param packageName The name of the package that was removed.
     90          * @param user The UserHandle of the profile that generated the change.
     91          */
     92         abstract public void onPackageRemoved(String packageName, UserHandle user);
     93 
     94         /**
     95          * Indicates that a package was added to the specified profile.
     96          *
     97          * If a package is added while being updated then onPackageChanged will be
     98          * called instead.
     99          *
    100          * @param packageName The name of the package that was added.
    101          * @param user The UserHandle of the profile that generated the change.
    102          */
    103         abstract public void onPackageAdded(String packageName, UserHandle user);
    104 
    105         /**
    106          * Indicates that a package was modified in the specified profile.
    107          * This can happen, for example, when the package is updated or when
    108          * one or more components are enabled or disabled.
    109          *
    110          * @param packageName The name of the package that has changed.
    111          * @param user The UserHandle of the profile that generated the change.
    112          */
    113         abstract public void onPackageChanged(String packageName, UserHandle user);
    114 
    115         /**
    116          * Indicates that one or more packages have become available. For
    117          * example, this can happen when a removable storage card has
    118          * reappeared.
    119          *
    120          * @param packageNames The names of the packages that have become
    121          *            available.
    122          * @param user The UserHandle of the profile that generated the change.
    123          * @param replacing Indicates whether these packages are replacing
    124          *            existing ones.
    125          */
    126         abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
    127                 boolean replacing);
    128 
    129         /**
    130          * Indicates that one or more packages have become unavailable. For
    131          * example, this can happen when a removable storage card has been
    132          * removed.
    133          *
    134          * @param packageNames The names of the packages that have become
    135          *            unavailable.
    136          * @param user The UserHandle of the profile that generated the change.
    137          * @param replacing Indicates whether the packages are about to be
    138          *            replaced with new versions.
    139          */
    140         abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
    141                 boolean replacing);
    142 
    143         /**
    144          * Indicates that one or more packages have been suspended. For
    145          * example, this can happen when a Device Administrator suspends
    146          * an applicaton.
    147          *
    148          * @param packageNames The names of the packages that have just been
    149          *            suspended.
    150          * @param user The UserHandle of the profile that generated the change.
    151          */
    152         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
    153         }
    154 
    155         /**
    156          * Indicates that one or more packages have been unsuspended. For
    157          * example, this can happen when a Device Administrator unsuspends
    158          * an applicaton.
    159          *
    160          * @param packageNames The names of the packages that have just been
    161          *            unsuspended.
    162          * @param user The UserHandle of the profile that generated the change.
    163          */
    164         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
    165         }
    166 
    167         /**
    168          * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest)
    169          * have been added, updated or removed.
    170          *
    171          * <p>Only the applications that are allowed to access the shortcut information,
    172          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
    173          *
    174          * @param packageName The name of the package that has the shortcuts.
    175          * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned).
    176          *    Only "key" information will be provided, as defined in
    177          *    {@link ShortcutInfo#hasKeyFieldsOnly()}.
    178          * @param user The UserHandle of the profile that generated the change.
    179          *
    180          * @see ShortcutManager
    181          */
    182         public void onShortcutsChanged(@NonNull String packageName,
    183                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
    184         }
    185     }
    186 
    187     /**
    188      * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
    189      */
    190     public static class ShortcutQuery {
    191         /**
    192          * Include dynamic shortcuts in the result.
    193          */
    194         public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
    195 
    196         /** @hide kept for unit tests */
    197         @Deprecated
    198         public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
    199 
    200         /**
    201          * Include pinned shortcuts in the result.
    202          */
    203         public static final int FLAG_MATCH_PINNED = 1 << 1;
    204 
    205         /** @hide kept for unit tests */
    206         @Deprecated
    207         public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
    208 
    209         /**
    210          * Include manifest shortcuts in the result.
    211          */
    212         public static final int FLAG_MATCH_MANIFEST = 1 << 3;
    213 
    214         /** @hide kept for unit tests */
    215         @Deprecated
    216         public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
    217 
    218         /** @hide */
    219         public static final int FLAG_MATCH_ALL_KINDS =
    220                 FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
    221 
    222         /** @hide kept for unit tests */
    223         @Deprecated
    224         public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
    225 
    226         /**
    227          * Requests "key" fields only.  See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to
    228          * see which fields fields "key".
    229          * This allows quicker access to shortcut information in order to
    230          * determine whether the caller's in-memory cache needs to be updated.
    231          *
    232          * <p>Typically, launcher applications cache all or most shortcut information
    233          * in memory in order to show shortcuts without a delay.
    234          *
    235          * When a given launcher application wants to update its cache, such as when its process
    236          * restarts, it can fetch shortcut information with this flag.
    237          * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
    238          * shortcut, fetching a shortcut's non-key information only if that shortcut has been
    239          * updated.
    240          *
    241          * @see ShortcutManager
    242          */
    243         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
    244 
    245         /** @hide */
    246         @IntDef(flag = true,
    247                 value = {
    248                         FLAG_MATCH_DYNAMIC,
    249                         FLAG_MATCH_PINNED,
    250                         FLAG_MATCH_MANIFEST,
    251                         FLAG_GET_KEY_FIELDS_ONLY,
    252                 })
    253         @Retention(RetentionPolicy.SOURCE)
    254         public @interface QueryFlags {}
    255 
    256         long mChangedSince;
    257 
    258         @Nullable
    259         String mPackage;
    260 
    261         @Nullable
    262         List<String> mShortcutIds;
    263 
    264         @Nullable
    265         ComponentName mActivity;
    266 
    267         @QueryFlags
    268         int mQueryFlags;
    269 
    270         public ShortcutQuery() {
    271         }
    272 
    273         /**
    274          * If non-zero, returns only shortcuts that have been added or updated
    275          * since the given timestamp, expressed in milliseconds since the Epoch&mdash;see
    276          * {@link System#currentTimeMillis()}.
    277          */
    278         public ShortcutQuery setChangedSince(long changedSince) {
    279             mChangedSince = changedSince;
    280             return this;
    281         }
    282 
    283         /**
    284          * If non-null, returns only shortcuts from the package.
    285          */
    286         public ShortcutQuery setPackage(@Nullable String packageName) {
    287             mPackage = packageName;
    288             return this;
    289         }
    290 
    291         /**
    292          * If non-null, return only the specified shortcuts by ID.  When setting this field,
    293          * a package name must also be set with {@link #setPackage}.
    294          */
    295         public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
    296             mShortcutIds = shortcutIds;
    297             return this;
    298         }
    299 
    300         /**
    301          * If non-null, returns only shortcuts associated with the activity; i.e.
    302          * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
    303          * to {@code activity}.
    304          */
    305         public ShortcutQuery setActivity(@Nullable ComponentName activity) {
    306             mActivity = activity;
    307             return this;
    308         }
    309 
    310         /**
    311          * Set query options.  At least one of the {@code MATCH} flags should be set.  Otherwise,
    312          * no shortcuts will be returned.
    313          *
    314          * <ul>
    315          *     <li>{@link #FLAG_MATCH_DYNAMIC}
    316          *     <li>{@link #FLAG_MATCH_PINNED}
    317          *     <li>{@link #FLAG_MATCH_MANIFEST}
    318          *     <li>{@link #FLAG_GET_KEY_FIELDS_ONLY}
    319          * </ul>
    320          */
    321         public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
    322             mQueryFlags = queryFlags;
    323             return this;
    324         }
    325     }
    326 
    327     /** @hide */
    328     public LauncherApps(Context context, ILauncherApps service) {
    329         mContext = context;
    330         mService = service;
    331         mPm = context.getPackageManager();
    332     }
    333 
    334     /** @hide */
    335     @TestApi
    336     public LauncherApps(Context context) {
    337         this(context, ILauncherApps.Stub.asInterface(
    338                 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
    339     }
    340 
    341     /**
    342      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
    343      * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
    344      *
    345      * @param packageName The specific package to query. If null, it checks all installed packages
    346      *            in the profile.
    347      * @param user The UserHandle of the profile.
    348      * @return List of launchable activities. Can be an empty list but will not be null.
    349      */
    350     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
    351         ParceledListSlice<ResolveInfo> activities = null;
    352         try {
    353             activities = mService.getLauncherActivities(packageName, user);
    354         } catch (RemoteException re) {
    355             throw re.rethrowFromSystemServer();
    356         }
    357         if (activities == null) {
    358             return Collections.EMPTY_LIST;
    359         }
    360         ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
    361         for (ResolveInfo ri : activities.getList()) {
    362             LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
    363             if (DEBUG) {
    364                 Log.v(TAG, "Returning activity for profile " + user + " : "
    365                         + lai.getComponentName());
    366             }
    367             lais.add(lai);
    368         }
    369         return lais;
    370     }
    371 
    372     /**
    373      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
    374      * returns null.
    375      *
    376      * @param intent The intent to find a match for.
    377      * @param user The profile to look in for a match.
    378      * @return An activity info object if there is a match.
    379      */
    380     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
    381         try {
    382             ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
    383             if (ai != null) {
    384                 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
    385                 return info;
    386             }
    387         } catch (RemoteException re) {
    388             throw re.rethrowFromSystemServer();
    389         }
    390         return null;
    391     }
    392 
    393     /**
    394      * Starts a Main activity in the specified profile.
    395      *
    396      * @param component The ComponentName of the activity to launch
    397      * @param user The UserHandle of the profile
    398      * @param sourceBounds The Rect containing the source bounds of the clicked icon
    399      * @param opts Options to pass to startActivity
    400      */
    401     public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
    402             Bundle opts) {
    403         if (DEBUG) {
    404             Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
    405         }
    406         try {
    407             mService.startActivityAsUser(component, sourceBounds, opts, user);
    408         } catch (RemoteException re) {
    409             throw re.rethrowFromSystemServer();
    410         }
    411     }
    412 
    413     /**
    414      * Starts the settings activity to show the application details for a
    415      * package in the specified profile.
    416      *
    417      * @param component The ComponentName of the package to launch settings for.
    418      * @param user The UserHandle of the profile
    419      * @param sourceBounds The Rect containing the source bounds of the clicked icon
    420      * @param opts Options to pass to startActivity
    421      */
    422     public void startAppDetailsActivity(ComponentName component, UserHandle user,
    423             Rect sourceBounds, Bundle opts) {
    424         try {
    425             mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
    426         } catch (RemoteException re) {
    427             throw re.rethrowFromSystemServer();
    428         }
    429     }
    430 
    431     /**
    432      * Checks if the package is installed and enabled for a profile.
    433      *
    434      * @param packageName The package to check.
    435      * @param user The UserHandle of the profile.
    436      *
    437      * @return true if the package exists and is enabled.
    438      */
    439     public boolean isPackageEnabled(String packageName, UserHandle user) {
    440         try {
    441             return mService.isPackageEnabled(packageName, user);
    442         } catch (RemoteException re) {
    443             throw re.rethrowFromSystemServer();
    444         }
    445     }
    446 
    447     /**
    448      * Retrieve all of the information we know about a particular package / application.
    449      *
    450      * @param packageName The package of the application
    451      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
    452      * @param user The UserHandle of the profile.
    453      *
    454      * @return An {@link ApplicationInfo} containing information about the package or
    455      *         null of the package isn't found.
    456      * @hide
    457      */
    458     public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
    459             UserHandle user) {
    460         try {
    461             return mService.getApplicationInfo(packageName, flags, user);
    462         } catch (RemoteException re) {
    463             throw re.rethrowFromSystemServer();
    464         }
    465     }
    466 
    467     /**
    468      * Checks if the activity exists and it enabled for a profile.
    469      *
    470      * @param component The activity to check.
    471      * @param user The UserHandle of the profile.
    472      *
    473      * @return true if the activity exists and is enabled.
    474      */
    475     public boolean isActivityEnabled(ComponentName component, UserHandle user) {
    476         try {
    477             return mService.isActivityEnabled(component, user);
    478         } catch (RemoteException re) {
    479             throw re.rethrowFromSystemServer();
    480         }
    481     }
    482 
    483     /**
    484      * Returns whether the caller can access the shortcut information.
    485      *
    486      * <p>Only the default launcher can access the shortcut information.
    487      *
    488      * <p>Note when this method returns {@code false}, it may be a temporary situation because
    489      * the user is trying a new launcher application.  The user may decide to change the default
    490      * launcher back to the calling application again, so even if a launcher application loses
    491      * this permission, it does <b>not</b> have to purge pinned shortcut information.
    492      * If the calling launcher application contains pinned shortcuts, they will still work,
    493      * even though the caller no longer has the shortcut host permission.
    494      *
    495      * @throws IllegalStateException when the user is locked.
    496      *
    497      * @see ShortcutManager
    498      */
    499     public boolean hasShortcutHostPermission() {
    500         try {
    501             return mService.hasShortcutHostPermission(mContext.getPackageName());
    502         } catch (RemoteException re) {
    503             throw re.rethrowFromSystemServer();
    504         }
    505     }
    506 
    507     /**
    508      * Returns {@link ShortcutInfo}s that match {@code query}.
    509      *
    510      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
    511      * #hasShortcutHostPermission()}.
    512      *
    513      * @param query result includes shortcuts matching this query.
    514      * @param user The UserHandle of the profile.
    515      *
    516      * @return the IDs of {@link ShortcutInfo}s that match the query.
    517      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    518      * is locked or not running.
    519      *
    520      * @see ShortcutManager
    521      */
    522     @Nullable
    523     public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
    524             @NonNull UserHandle user) {
    525         try {
    526             return mService.getShortcuts(mContext.getPackageName(),
    527                     query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
    528                     query.mQueryFlags, user)
    529                     .getList();
    530         } catch (RemoteException e) {
    531             throw e.rethrowFromSystemServer();
    532         }
    533     }
    534 
    535     /**
    536      * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
    537      */
    538     @Nullable
    539     @Deprecated
    540     public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
    541             @NonNull List<String> ids, @NonNull UserHandle user) {
    542         final ShortcutQuery q = new ShortcutQuery();
    543         q.setPackage(packageName);
    544         q.setShortcutIds(ids);
    545         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
    546         return getShortcuts(q, user);
    547     }
    548 
    549     /**
    550      * Pin shortcuts on a package.
    551      *
    552      * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
    553      * However, different launchers may have different set of pinned shortcuts.
    554      *
    555      * <p>The calling launcher application must be allowed to access the shortcut information,
    556      * as defined in {@link #hasShortcutHostPermission()}.
    557      *
    558      * @param packageName The target package name.
    559      * @param shortcutIds The IDs of the shortcut to be pinned.
    560      * @param user The UserHandle of the profile.
    561      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    562      * is locked or not running.
    563      *
    564      * @see ShortcutManager
    565      */
    566     public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
    567             @NonNull UserHandle user) {
    568         try {
    569             mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
    570         } catch (RemoteException e) {
    571             throw e.rethrowFromSystemServer();
    572         }
    573     }
    574 
    575     /**
    576      * @hide kept for testing.
    577      */
    578     @Deprecated
    579     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
    580         return shortcut.getIconResourceId();
    581     }
    582 
    583     /**
    584      * @hide kept for testing.
    585      */
    586     @Deprecated
    587     public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
    588             @NonNull UserHandle user) {
    589         final ShortcutQuery q = new ShortcutQuery();
    590         q.setPackage(packageName);
    591         q.setShortcutIds(Arrays.asList(shortcutId));
    592         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
    593         final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
    594 
    595         return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
    596     }
    597 
    598     /**
    599      * @hide internal/unit tests only
    600      */
    601     public ParcelFileDescriptor getShortcutIconFd(
    602             @NonNull ShortcutInfo shortcut) {
    603         return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
    604                 shortcut.getUserId());
    605     }
    606 
    607     /**
    608      * @hide internal/unit tests only
    609      */
    610     public ParcelFileDescriptor getShortcutIconFd(
    611             @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
    612         return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
    613     }
    614 
    615     private ParcelFileDescriptor getShortcutIconFd(
    616             @NonNull String packageName, @NonNull String shortcutId, int userId) {
    617         try {
    618             return mService.getShortcutIconFd(mContext.getPackageName(),
    619                     packageName, shortcutId, userId);
    620         } catch (RemoteException e) {
    621             throw e.rethrowFromSystemServer();
    622         }
    623     }
    624 
    625     /**
    626      * Returns the icon for this shortcut, without any badging for the profile.
    627      *
    628      * <p>The calling launcher application must be allowed to access the shortcut information,
    629      * as defined in {@link #hasShortcutHostPermission()}.
    630      *
    631      * @param density The preferred density of the icon, zero for default density. Use
    632      * density DPI values from {@link DisplayMetrics}.
    633      *
    634      * @return The drawable associated with the shortcut.
    635      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    636      * is locked or not running.
    637      *
    638      * @see ShortcutManager
    639      * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
    640      * @see DisplayMetrics
    641      */
    642     public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
    643         if (shortcut.hasIconFile()) {
    644             final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
    645             if (pfd == null) {
    646                 return null;
    647             }
    648             try {
    649                 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
    650                 return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp);
    651             } finally {
    652                 try {
    653                     pfd.close();
    654                 } catch (IOException ignore) {
    655                 }
    656             }
    657         } else if (shortcut.hasIconResource()) {
    658             try {
    659                 final int resId = shortcut.getIconResourceId();
    660                 if (resId == 0) {
    661                     return null; // Shouldn't happen but just in case.
    662                 }
    663                 final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
    664                         /* flags =*/ 0, shortcut.getUserHandle());
    665                 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
    666                 return res.getDrawableForDensity(resId, density);
    667             } catch (NameNotFoundException | Resources.NotFoundException e) {
    668                 return null;
    669             }
    670         } else {
    671             return null; // Has no icon.
    672         }
    673     }
    674 
    675     /**
    676      * Returns the shortcut icon with badging appropriate for the profile.
    677      *
    678      * <p>The calling launcher application must be allowed to access the shortcut information,
    679      * as defined in {@link #hasShortcutHostPermission()}.
    680      *
    681      * @param density Optional density for the icon, or 0 to use the default density. Use
    682      * @return A badged icon for the shortcut.
    683      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    684      * is locked or not running.
    685      *
    686      * @see ShortcutManager
    687      * @see #getShortcutIconDrawable(ShortcutInfo, int)
    688      * @see DisplayMetrics
    689      */
    690     public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
    691         final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
    692 
    693         return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
    694                 originalIcon, shortcut.getUserHandle());
    695     }
    696 
    697     /**
    698      * Starts a shortcut.
    699      *
    700      * <p>The calling launcher application must be allowed to access the shortcut information,
    701      * as defined in {@link #hasShortcutHostPermission()}.
    702      *
    703      * @param packageName The target shortcut package name.
    704      * @param shortcutId The target shortcut ID.
    705      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
    706      * @param startActivityOptions Options to pass to startActivity.
    707      * @param user The UserHandle of the profile.
    708      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    709      * is locked or not running.
    710      *
    711      * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
    712      * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
    713      */
    714     public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
    715             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
    716             @NonNull UserHandle user) {
    717         startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
    718                 user.getIdentifier());
    719     }
    720 
    721     /**
    722      * Launches a shortcut.
    723      *
    724      * <p>The calling launcher application must be allowed to access the shortcut information,
    725      * as defined in {@link #hasShortcutHostPermission()}.
    726      *
    727      * @param shortcut The target shortcut.
    728      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
    729      * @param startActivityOptions Options to pass to startActivity.
    730      * @throws IllegalStateException when the user is locked, or when the {@code user} user
    731      * is locked or not running.
    732      *
    733      * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
    734      * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
    735      */
    736     public void startShortcut(@NonNull ShortcutInfo shortcut,
    737             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
    738         startShortcut(shortcut.getPackage(), shortcut.getId(),
    739                 sourceBounds, startActivityOptions,
    740                 shortcut.getUserId());
    741     }
    742 
    743     private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
    744             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
    745             int userId) {
    746         try {
    747             final boolean success =
    748                     mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
    749                     sourceBounds, startActivityOptions, userId);
    750             if (!success) {
    751                 throw new ActivityNotFoundException("Shortcut could not be started");
    752             }
    753         } catch (RemoteException e) {
    754             throw e.rethrowFromSystemServer();
    755         }
    756     }
    757 
    758     /**
    759      * Registers a callback for changes to packages in current and managed profiles.
    760      *
    761      * @param callback The callback to register.
    762      */
    763     public void registerCallback(Callback callback) {
    764         registerCallback(callback, null);
    765     }
    766 
    767     /**
    768      * Registers a callback for changes to packages in current and managed profiles.
    769      *
    770      * @param callback The callback to register.
    771      * @param handler that should be used to post callbacks on, may be null.
    772      */
    773     public void registerCallback(Callback callback, Handler handler) {
    774         synchronized (this) {
    775             if (callback != null && findCallbackLocked(callback) < 0) {
    776                 boolean addedFirstCallback = mCallbacks.size() == 0;
    777                 addCallbackLocked(callback, handler);
    778                 if (addedFirstCallback) {
    779                     try {
    780                         mService.addOnAppsChangedListener(mContext.getPackageName(),
    781                                 mAppsChangedListener);
    782                     } catch (RemoteException re) {
    783                         throw re.rethrowFromSystemServer();
    784                     }
    785                 }
    786             }
    787         }
    788     }
    789 
    790     /**
    791      * Unregisters a callback that was previously registered.
    792      *
    793      * @param callback The callback to unregister.
    794      * @see #registerCallback(Callback)
    795      */
    796     public void unregisterCallback(Callback callback) {
    797         synchronized (this) {
    798             removeCallbackLocked(callback);
    799             if (mCallbacks.size() == 0) {
    800                 try {
    801                     mService.removeOnAppsChangedListener(mAppsChangedListener);
    802                 } catch (RemoteException re) {
    803                     throw re.rethrowFromSystemServer();
    804                 }
    805             }
    806         }
    807     }
    808 
    809     /** @return position in mCallbacks for callback or -1 if not present. */
    810     private int findCallbackLocked(Callback callback) {
    811         if (callback == null) {
    812             throw new IllegalArgumentException("Callback cannot be null");
    813         }
    814         final int size = mCallbacks.size();
    815         for (int i = 0; i < size; ++i) {
    816             if (mCallbacks.get(i).mCallback == callback) {
    817                 return i;
    818             }
    819         }
    820         return -1;
    821     }
    822 
    823     private void removeCallbackLocked(Callback callback) {
    824         int pos = findCallbackLocked(callback);
    825         if (pos >= 0) {
    826             mCallbacks.remove(pos);
    827         }
    828     }
    829 
    830     private void addCallbackLocked(Callback callback, Handler handler) {
    831         // Remove if already present.
    832         removeCallbackLocked(callback);
    833         if (handler == null) {
    834             handler = new Handler();
    835         }
    836         CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
    837         mCallbacks.add(toAdd);
    838     }
    839 
    840     private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
    841 
    842         @Override
    843         public void onPackageRemoved(UserHandle user, String packageName)
    844                 throws RemoteException {
    845             if (DEBUG) {
    846                 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
    847             }
    848             synchronized (LauncherApps.this) {
    849                 for (CallbackMessageHandler callback : mCallbacks) {
    850                     callback.postOnPackageRemoved(packageName, user);
    851                 }
    852             }
    853         }
    854 
    855         @Override
    856         public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
    857             if (DEBUG) {
    858                 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
    859             }
    860             synchronized (LauncherApps.this) {
    861                 for (CallbackMessageHandler callback : mCallbacks) {
    862                     callback.postOnPackageChanged(packageName, user);
    863                 }
    864             }
    865         }
    866 
    867         @Override
    868         public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
    869             if (DEBUG) {
    870                 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
    871             }
    872             synchronized (LauncherApps.this) {
    873                 for (CallbackMessageHandler callback : mCallbacks) {
    874                     callback.postOnPackageAdded(packageName, user);
    875                 }
    876             }
    877         }
    878 
    879         @Override
    880         public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
    881                 throws RemoteException {
    882             if (DEBUG) {
    883                 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
    884             }
    885             synchronized (LauncherApps.this) {
    886                 for (CallbackMessageHandler callback : mCallbacks) {
    887                     callback.postOnPackagesAvailable(packageNames, user, replacing);
    888                 }
    889             }
    890         }
    891 
    892         @Override
    893         public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
    894                 throws RemoteException {
    895             if (DEBUG) {
    896                 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
    897             }
    898             synchronized (LauncherApps.this) {
    899                 for (CallbackMessageHandler callback : mCallbacks) {
    900                     callback.postOnPackagesUnavailable(packageNames, user, replacing);
    901                 }
    902             }
    903         }
    904 
    905         @Override
    906         public void onPackagesSuspended(UserHandle user, String[] packageNames)
    907                 throws RemoteException {
    908             if (DEBUG) {
    909                 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
    910             }
    911             synchronized (LauncherApps.this) {
    912                 for (CallbackMessageHandler callback : mCallbacks) {
    913                     callback.postOnPackagesSuspended(packageNames, user);
    914                 }
    915             }
    916         }
    917 
    918         @Override
    919         public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
    920                 throws RemoteException {
    921             if (DEBUG) {
    922                 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
    923             }
    924             synchronized (LauncherApps.this) {
    925                 for (CallbackMessageHandler callback : mCallbacks) {
    926                     callback.postOnPackagesUnsuspended(packageNames, user);
    927                 }
    928             }
    929         }
    930 
    931         @Override
    932         public void onShortcutChanged(UserHandle user, String packageName,
    933                 ParceledListSlice shortcuts) {
    934             if (DEBUG) {
    935                 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
    936             }
    937             final List<ShortcutInfo> list = shortcuts.getList();
    938             synchronized (LauncherApps.this) {
    939                 for (CallbackMessageHandler callback : mCallbacks) {
    940                     callback.postOnShortcutChanged(packageName, user, list);
    941                 }
    942             }
    943         }
    944     };
    945 
    946     private static class CallbackMessageHandler extends Handler {
    947         private static final int MSG_ADDED = 1;
    948         private static final int MSG_REMOVED = 2;
    949         private static final int MSG_CHANGED = 3;
    950         private static final int MSG_AVAILABLE = 4;
    951         private static final int MSG_UNAVAILABLE = 5;
    952         private static final int MSG_SUSPENDED = 6;
    953         private static final int MSG_UNSUSPENDED = 7;
    954         private static final int MSG_SHORTCUT_CHANGED = 8;
    955 
    956         private LauncherApps.Callback mCallback;
    957 
    958         private static class CallbackInfo {
    959             String[] packageNames;
    960             String packageName;
    961             boolean replacing;
    962             UserHandle user;
    963             List<ShortcutInfo> shortcuts;
    964         }
    965 
    966         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
    967             super(looper, null, true);
    968             mCallback = callback;
    969         }
    970 
    971         @Override
    972         public void handleMessage(Message msg) {
    973             if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
    974                 return;
    975             }
    976             CallbackInfo info = (CallbackInfo) msg.obj;
    977             switch (msg.what) {
    978                 case MSG_ADDED:
    979                     mCallback.onPackageAdded(info.packageName, info.user);
    980                     break;
    981                 case MSG_REMOVED:
    982                     mCallback.onPackageRemoved(info.packageName, info.user);
    983                     break;
    984                 case MSG_CHANGED:
    985                     mCallback.onPackageChanged(info.packageName, info.user);
    986                     break;
    987                 case MSG_AVAILABLE:
    988                     mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
    989                     break;
    990                 case MSG_UNAVAILABLE:
    991                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
    992                     break;
    993                 case MSG_SUSPENDED:
    994                     mCallback.onPackagesSuspended(info.packageNames, info.user);
    995                     break;
    996                 case MSG_UNSUSPENDED:
    997                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
    998                     break;
    999                 case MSG_SHORTCUT_CHANGED:
   1000                     mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
   1001                     break;
   1002             }
   1003         }
   1004 
   1005         public void postOnPackageAdded(String packageName, UserHandle user) {
   1006             CallbackInfo info = new CallbackInfo();
   1007             info.packageName = packageName;
   1008             info.user = user;
   1009             obtainMessage(MSG_ADDED, info).sendToTarget();
   1010         }
   1011 
   1012         public void postOnPackageRemoved(String packageName, UserHandle user) {
   1013             CallbackInfo info = new CallbackInfo();
   1014             info.packageName = packageName;
   1015             info.user = user;
   1016             obtainMessage(MSG_REMOVED, info).sendToTarget();
   1017         }
   1018 
   1019         public void postOnPackageChanged(String packageName, UserHandle user) {
   1020             CallbackInfo info = new CallbackInfo();
   1021             info.packageName = packageName;
   1022             info.user = user;
   1023             obtainMessage(MSG_CHANGED, info).sendToTarget();
   1024         }
   1025 
   1026         public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
   1027                 boolean replacing) {
   1028             CallbackInfo info = new CallbackInfo();
   1029             info.packageNames = packageNames;
   1030             info.replacing = replacing;
   1031             info.user = user;
   1032             obtainMessage(MSG_AVAILABLE, info).sendToTarget();
   1033         }
   1034 
   1035         public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
   1036                 boolean replacing) {
   1037             CallbackInfo info = new CallbackInfo();
   1038             info.packageNames = packageNames;
   1039             info.replacing = replacing;
   1040             info.user = user;
   1041             obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
   1042         }
   1043 
   1044         public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
   1045             CallbackInfo info = new CallbackInfo();
   1046             info.packageNames = packageNames;
   1047             info.user = user;
   1048             obtainMessage(MSG_SUSPENDED, info).sendToTarget();
   1049         }
   1050 
   1051         public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
   1052             CallbackInfo info = new CallbackInfo();
   1053             info.packageNames = packageNames;
   1054             info.user = user;
   1055             obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
   1056         }
   1057 
   1058         public void postOnShortcutChanged(String packageName, UserHandle user,
   1059                 List<ShortcutInfo> shortcuts) {
   1060             CallbackInfo info = new CallbackInfo();
   1061             info.packageName = packageName;
   1062             info.user = user;
   1063             info.shortcuts = shortcuts;
   1064             obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
   1065         }
   1066     }
   1067 }
   1068