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