1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.pm;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SdkConstant;
23 import android.annotation.SdkConstant.SdkConstantType;
24 import android.annotation.SystemService;
25 import android.annotation.TestApi;
26 import android.app.PendingIntent;
27 import android.appwidget.AppWidgetManager;
28 import android.appwidget.AppWidgetProviderInfo;
29 import android.content.ActivityNotFoundException;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentSender;
34 import android.content.pm.PackageManager.ApplicationInfoFlags;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.content.res.Resources;
37 import android.graphics.Bitmap;
38 import android.graphics.BitmapFactory;
39 import android.graphics.Rect;
40 import android.graphics.drawable.AdaptiveIconDrawable;
41 import android.graphics.drawable.BitmapDrawable;
42 import android.graphics.drawable.Drawable;
43 import android.graphics.drawable.Icon;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.Parcel;
49 import android.os.ParcelFileDescriptor;
50 import android.os.Parcelable;
51 import android.os.RemoteException;
52 import android.os.ServiceManager;
53 import android.os.UserHandle;
54 import android.os.UserManager;
55 import android.util.DisplayMetrics;
56 import android.util.Log;
57 
58 import com.android.internal.util.Preconditions;
59 
60 import java.io.IOException;
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Collections;
66 import java.util.List;
67 
68 /**
69  * Class for retrieving a list of launchable activities for the current user and any associated
70  * managed profiles that are visible to the current user, which can be retrieved with
71  * {@link #getProfiles}. This is mainly for use by launchers.
72  *
73  * Apps can be queried for each user profile.
74  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
75  * for package changes here.
76  * <p>
77  * To watch for managed profiles being added or removed, register for the following broadcasts:
78  * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
79  * <p>
80  * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the
81  * main profile.  Apps can only access profiles returned by {@link #getProfiles()}.
82  */
83 @SystemService(Context.LAUNCHER_APPS_SERVICE)
84 public class LauncherApps {
85 
86     static final String TAG = "LauncherApps";
87     static final boolean DEBUG = false;
88 
89     /**
90      * Activity Action: For the default launcher to show the confirmation dialog to create
91      * a pinned shortcut.
92      *
93      * <p>See the {@link ShortcutManager} javadoc for details.
94      *
95      * <p>
96      * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
97      * and call {@link PinItemRequest#accept(Bundle)}
98      * if the user accepts.  If the user doesn't accept, no further action is required.
99      *
100      * @see #EXTRA_PIN_ITEM_REQUEST
101      */
102     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
103     public static final String ACTION_CONFIRM_PIN_SHORTCUT =
104             "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
105 
106     /**
107      * Activity Action: For the default launcher to show the confirmation dialog to create
108      * a pinned app widget.
109      *
110      * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for
111      * details.
112      *
113      * <p>
114      * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
115      * and call {@link PinItemRequest#accept(Bundle)}
116      * if the user accepts.  If the user doesn't accept, no further action is required.
117      *
118      * @see #EXTRA_PIN_ITEM_REQUEST
119      */
120     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
121     public static final String ACTION_CONFIRM_PIN_APPWIDGET =
122             "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
123 
124     /**
125      * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} &amp; {@link #ACTION_CONFIRM_PIN_APPWIDGET}
126      * containing a {@link PinItemRequest} of appropriate type asked to pin.
127      *
128      * <p>A helper function {@link #getPinItemRequest(Intent)} can be used
129      * instead of using this constant directly.
130      *
131      * @see #ACTION_CONFIRM_PIN_SHORTCUT
132      * @see #ACTION_CONFIRM_PIN_APPWIDGET
133      */
134     public static final String EXTRA_PIN_ITEM_REQUEST =
135             "android.content.pm.extra.PIN_ITEM_REQUEST";
136 
137     private final Context mContext;
138     private final ILauncherApps mService;
139     private final PackageManager mPm;
140     private final UserManager mUserManager;
141 
142     private List<CallbackMessageHandler> mCallbacks
143             = new ArrayList<CallbackMessageHandler>();
144 
145     /**
146      * Callbacks for package changes to this and related managed profiles.
147      */
148     public static abstract class Callback {
149         /**
150          * Indicates that a package was removed from the specified profile.
151          *
152          * If a package is removed while being updated onPackageChanged will be
153          * called instead.
154          *
155          * @param packageName The name of the package that was removed.
156          * @param user The UserHandle of the profile that generated the change.
157          */
onPackageRemoved(String packageName, UserHandle user)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          */
onPackageAdded(String packageName, UserHandle user)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          */
onPackageChanged(String packageName, UserHandle user)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          */
onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)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          */
onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)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          */
onPackagesSuspended(String[] packageNames, UserHandle user)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          */
onPackagesSuspended(String[] packageNames, UserHandle user, @Nullable Bundle launcherExtras)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          */
onPackagesUnsuspended(String[] packageNames, UserHandle user)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          */
onShortcutsChanged(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)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 
ShortcutQuery()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          */
setChangedSince(long changedSince)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          */
setPackage(@ullable String packageName)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          */
setShortcutIds(@ullable List<String> shortcutIds)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          */
setActivity(@ullable ComponentName activity)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          */
setQueryFlags(@ueryFlags int queryFlags)446         public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
447             mQueryFlags = queryFlags;
448             return this;
449         }
450     }
451 
452     /** @hide */
LauncherApps(Context context, ILauncherApps service)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
LauncherApps(Context context)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      */
logErrorForInvalidProfileAccess(@onNull UserHandle target)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      */
getProfiles()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      */
getActivityList(String packageName, UserHandle user)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      */
resolveActivity(Intent intent, UserHandle user)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      */
startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)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      */
startAppDetailsActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)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      */
getShortcutConfigActivityList(@ullable String packageName, @NonNull UserHandle user)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 
convertToActivityList( @ullable ParceledListSlice<ResolveInfo> activities, UserHandle user)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
getShortcutConfigActivityIntent(@onNull LauncherActivityInfo info)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      */
isPackageEnabled(String packageName, UserHandle user)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      */
getSuspendedPackageLauncherExtras(String packageName, UserHandle user)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      */
getApplicationInfo(@onNull String packageName, @ApplicationInfoFlags int flags, @NonNull UserHandle user)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      */
isActivityEnabled(ComponentName component, UserHandle user)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      */
hasShortcutHostPermission()762     public boolean hasShortcutHostPermission() {
763         try {
764             return mService.hasShortcutHostPermission(mContext.getPackageName());
765         } catch (RemoteException re) {
766             throw re.rethrowFromSystemServer();
767         }
768     }
769 
maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts)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
getShortcuts(@onNull ShortcutQuery query, @NonNull UserHandle user)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
getShortcutInfo(@onNull String packageName, @NonNull List<String> ids, @NonNull UserHandle user)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      */
pinShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user)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
getShortcutIconResId(@onNull ShortcutInfo shortcut)865     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
866         return shortcut.getIconResourceId();
867     }
868 
869     /**
870      * @hide kept for testing.
871      */
872     @Deprecated
getShortcutIconResId(@onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)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      */
getShortcutIconFd( @onNull ShortcutInfo shortcut)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      */
getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)896     public ParcelFileDescriptor getShortcutIconFd(
897             @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
898         return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
899     }
900 
getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, int userId)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      */
getShortcutIconDrawable(@onNull ShortcutInfo shortcut, int density)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 
loadDrawableResourceFromPackage(String packageName, int resId, UserHandle user, int density)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      */
getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density)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      */
startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user)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      */
startShortcut(@onNull ShortcutInfo shortcut, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions)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 
startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, int userId)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      */
registerCallback(Callback callback)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      */
registerCallback(Callback callback, Handler handler)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      */
unregisterCallback(Callback callback)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. */
findCallbackLocked(Callback callback)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 
removeCallbackLocked(Callback callback)1138     private void removeCallbackLocked(Callback callback) {
1139         int pos = findCallbackLocked(callback);
1140         if (pos >= 0) {
1141             mCallbacks.remove(pos);
1142         }
1143     }
1144 
addCallbackLocked(Callback callback, Handler handler)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 
CallbackMessageHandler(Looper looper, LauncherApps.Callback callback)1283         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
1284             super(looper, null, true);
1285             mCallback = callback;
1286         }
1287 
1288         @Override
handleMessage(Message msg)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 
postOnPackageAdded(String packageName, UserHandle user)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 
postOnPackageRemoved(String packageName, UserHandle user)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 
postOnPackageChanged(String packageName, UserHandle user)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 
postOnPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)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 
postOnPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)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 
postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, UserHandle user)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 
postOnPackagesUnsuspended(String[] packageNames, UserHandle user)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 
postOnShortcutChanged(String packageName, UserHandle user, List<ShortcutInfo> shortcuts)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      */
getPinItemRequest(Intent intent)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          */
PinItemRequest(IPinItemRequest inner, int type)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
getRequestType()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
getShortcutInfo()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
getAppWidgetProviderInfo(Context context)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
getExtras()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          */
isValid()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          */
accept(@ullable Bundle options)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          */
accept()1567         public boolean accept() {
1568             return accept(/* options= */ null);
1569         }
1570 
PinItemRequest(Parcel source)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
writeToParcel(Parcel dest, int flags)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
describeContents()1595         public int describeContents() {
1596             return 0;
1597         }
1598     }
1599 }
1600