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