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