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