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 static android.Manifest.permission;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SdkConstant;
27 import android.annotation.SdkConstant.SdkConstantType;
28 import android.annotation.SystemApi;
29 import android.annotation.SystemService;
30 import android.annotation.TestApi;
31 import android.app.PendingIntent;
32 import android.appwidget.AppWidgetManager;
33 import android.appwidget.AppWidgetProviderInfo;
34 import android.compat.annotation.UnsupportedAppUsage;
35 import android.content.ActivityNotFoundException;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentSender;
40 import android.content.LocusId;
41 import android.content.pm.PackageInstaller.SessionCallback;
42 import android.content.pm.PackageInstaller.SessionCallbackDelegate;
43 import android.content.pm.PackageInstaller.SessionInfo;
44 import android.content.pm.PackageManager.ApplicationInfoFlags;
45 import android.content.pm.PackageManager.NameNotFoundException;
46 import android.content.res.Resources;
47 import android.graphics.Bitmap;
48 import android.graphics.BitmapFactory;
49 import android.graphics.Rect;
50 import android.graphics.drawable.AdaptiveIconDrawable;
51 import android.graphics.drawable.BitmapDrawable;
52 import android.graphics.drawable.Drawable;
53 import android.graphics.drawable.Icon;
54 import android.net.Uri;
55 import android.os.Build;
56 import android.os.Bundle;
57 import android.os.Handler;
58 import android.os.Looper;
59 import android.os.Message;
60 import android.os.Parcel;
61 import android.os.ParcelFileDescriptor;
62 import android.os.Parcelable;
63 import android.os.RemoteException;
64 import android.os.ServiceManager;
65 import android.os.UserHandle;
66 import android.os.UserManager;
67 import android.util.DisplayMetrics;
68 import android.util.Log;
69 import android.util.Pair;
70 
71 import com.android.internal.annotations.VisibleForTesting;
72 import com.android.internal.util.function.pooled.PooledLambda;
73 
74 import java.io.FileNotFoundException;
75 import java.io.IOException;
76 import java.lang.annotation.Retention;
77 import java.lang.annotation.RetentionPolicy;
78 import java.lang.ref.WeakReference;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.Collections;
82 import java.util.HashMap;
83 import java.util.Iterator;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.Objects;
87 import java.util.concurrent.Executor;
88 
89 /**
90  * Class for retrieving a list of launchable activities for the current user and any associated
91  * managed profiles that are visible to the current user, which can be retrieved with
92  * {@link #getProfiles}. This is mainly for use by launchers.
93  *
94  * Apps can be queried for each user profile.
95  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
96  * for package changes here.
97  * <p>
98  * To watch for managed profiles being added or removed, register for the following broadcasts:
99  * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
100  * <p>
101  * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the
102  * main profile.  Apps can only access profiles returned by {@link #getProfiles()}.
103  */
104 @SystemService(Context.LAUNCHER_APPS_SERVICE)
105 public class LauncherApps {
106 
107     static final String TAG = "LauncherApps";
108     static final boolean DEBUG = false;
109 
110     /**
111      * Activity Action: For the default launcher to show the confirmation dialog to create
112      * a pinned shortcut.
113      *
114      * <p>See the {@link ShortcutManager} javadoc for details.
115      *
116      * <p>
117      * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
118      * and call {@link PinItemRequest#accept(Bundle)}
119      * if the user accepts.  If the user doesn't accept, no further action is required.
120      *
121      * @see #EXTRA_PIN_ITEM_REQUEST
122      */
123     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
124     public static final String ACTION_CONFIRM_PIN_SHORTCUT =
125             "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
126 
127     /**
128      * Activity Action: For the default launcher to show the confirmation dialog to create
129      * a pinned app widget.
130      *
131      * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for
132      * details.
133      *
134      * <p>
135      * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
136      * and call {@link PinItemRequest#accept(Bundle)}
137      * if the user accepts.  If the user doesn't accept, no further action is required.
138      *
139      * @see #EXTRA_PIN_ITEM_REQUEST
140      */
141     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
142     public static final String ACTION_CONFIRM_PIN_APPWIDGET =
143             "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
144 
145     /**
146      * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} &amp; {@link #ACTION_CONFIRM_PIN_APPWIDGET}
147      * containing a {@link PinItemRequest} of appropriate type asked to pin.
148      *
149      * <p>A helper function {@link #getPinItemRequest(Intent)} can be used
150      * instead of using this constant directly.
151      *
152      * @see #ACTION_CONFIRM_PIN_SHORTCUT
153      * @see #ACTION_CONFIRM_PIN_APPWIDGET
154      */
155     public static final String EXTRA_PIN_ITEM_REQUEST =
156             "android.content.pm.extra.PIN_ITEM_REQUEST";
157 
158     /**
159      * Cache shortcuts which are used in notifications.
160      * @hide
161      */
162     public static final int FLAG_CACHE_NOTIFICATION_SHORTCUTS = 0;
163 
164     /**
165      * Cache shortcuts which are used in bubbles.
166      * @hide
167      */
168     public static final int FLAG_CACHE_BUBBLE_SHORTCUTS = 1;
169 
170     /** @hide */
171     @IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = {
172             FLAG_CACHE_NOTIFICATION_SHORTCUTS,
173             FLAG_CACHE_BUBBLE_SHORTCUTS,
174     })
175     @Retention(RetentionPolicy.SOURCE)
176     public @interface ShortcutCacheFlags {}
177 
178     private final Context mContext;
179     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
180     private final ILauncherApps mService;
181     @UnsupportedAppUsage
182     private final PackageManager mPm;
183     private final UserManager mUserManager;
184 
185     private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
186     private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();
187 
188     private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>>
189             mShortcutChangeCallbacks = new HashMap<>();
190 
191     /**
192      * Callbacks for package changes to this and related managed profiles.
193      */
194     public static abstract class Callback {
195         /**
196          * Indicates that a package was removed from the specified profile.
197          *
198          * If a package is removed while being updated onPackageChanged will be
199          * called instead.
200          *
201          * @param packageName The name of the package that was removed.
202          * @param user The UserHandle of the profile that generated the change.
203          */
onPackageRemoved(String packageName, UserHandle user)204         abstract public void onPackageRemoved(String packageName, UserHandle user);
205 
206         /**
207          * Indicates that a package was added to the specified profile.
208          *
209          * If a package is added while being updated then onPackageChanged will be
210          * called instead.
211          *
212          * @param packageName The name of the package that was added.
213          * @param user The UserHandle of the profile that generated the change.
214          */
onPackageAdded(String packageName, UserHandle user)215         abstract public void onPackageAdded(String packageName, UserHandle user);
216 
217         /**
218          * Indicates that a package was modified in the specified profile.
219          * This can happen, for example, when the package is updated or when
220          * one or more components are enabled or disabled.
221          *
222          * @param packageName The name of the package that has changed.
223          * @param user The UserHandle of the profile that generated the change.
224          */
onPackageChanged(String packageName, UserHandle user)225         abstract public void onPackageChanged(String packageName, UserHandle user);
226 
227         /**
228          * Indicates that one or more packages have become available. For
229          * example, this can happen when a removable storage card has
230          * reappeared.
231          *
232          * @param packageNames The names of the packages that have become
233          *            available.
234          * @param user The UserHandle of the profile that generated the change.
235          * @param replacing Indicates whether these packages are replacing
236          *            existing ones.
237          */
onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)238         abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
239                 boolean replacing);
240 
241         /**
242          * Indicates that one or more packages have become unavailable. For
243          * example, this can happen when a removable storage card has been
244          * removed.
245          *
246          * @param packageNames The names of the packages that have become
247          *            unavailable.
248          * @param user The UserHandle of the profile that generated the change.
249          * @param replacing Indicates whether the packages are about to be
250          *            replaced with new versions.
251          */
onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)252         abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
253                 boolean replacing);
254 
255         /**
256          * Indicates that one or more packages have been suspended. For
257          * example, this can happen when a Device Administrator suspends
258          * an applicaton.
259          *
260          * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
261          * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
262          * not receive this callback.
263          *
264          * @param packageNames The names of the packages that have just been
265          *            suspended.
266          * @param user The UserHandle of the profile that generated the change.
267          */
onPackagesSuspended(String[] packageNames, UserHandle user)268         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
269         }
270 
271         /**
272          * Indicates that one or more packages have been suspended. A device administrator or an app
273          * with {@code android.permission.SUSPEND_APPS} can do this.
274          *
275          * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
276          * optionally provide a {@link Bundle} of extra information that it deems helpful for the
277          * launcher to handle the suspended state of these packages. The contents of this
278          * {@link Bundle} are supposed to be a contract between the suspending app and the launcher.
279          *
280          * @param packageNames The names of the packages that have just been suspended.
281          * @param user the user for which the given packages were suspended.
282          * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
283          *                      system, {@code null} otherwise.
284          * @see PackageManager#isPackageSuspended()
285          * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
286          * @deprecated {@code launcherExtras} should be obtained by using
287          * {@link #getSuspendedPackageLauncherExtras(String, UserHandle)}. For all other cases,
288          * {@link #onPackagesSuspended(String[], UserHandle)} should be used.
289          */
290         @Deprecated
onPackagesSuspended(String[] packageNames, UserHandle user, @Nullable Bundle launcherExtras)291         public void onPackagesSuspended(String[] packageNames, UserHandle user,
292                 @Nullable Bundle launcherExtras) {
293             onPackagesSuspended(packageNames, user);
294         }
295 
296         /**
297          * Indicates that one or more packages have been unsuspended. For
298          * example, this can happen when a Device Administrator unsuspends
299          * an applicaton.
300          *
301          * @param packageNames The names of the packages that have just been
302          *            unsuspended.
303          * @param user The UserHandle of the profile that generated the change.
304          */
onPackagesUnsuspended(String[] packageNames, UserHandle user)305         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
306         }
307 
308         /**
309          * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest)
310          * have been added, updated or removed.
311          *
312          * <p>Only the applications that are allowed to access the shortcut information,
313          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
314          *
315          * @param packageName The name of the package that has the shortcuts.
316          * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned).
317          *    Only "key" information will be provided, as defined in
318          *    {@link ShortcutInfo#hasKeyFieldsOnly()}.
319          * @param user The UserHandle of the profile that generated the change.
320          *
321          * @see ShortcutManager
322          */
onShortcutsChanged(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)323         public void onShortcutsChanged(@NonNull String packageName,
324                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
325         }
326     }
327 
328     /**
329      * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
330      */
331     public static class ShortcutQuery {
332         /**
333          * Include dynamic shortcuts in the result.
334          */
335         public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
336 
337         /** @hide kept for unit tests */
338         @Deprecated
339         public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
340 
341         /**
342          * Include pinned shortcuts in the result.
343          *
344          * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
345          * user owns on the launcher (or by other launchers, in case the user has multiple), use
346          * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
347          *
348          * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other
349          * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this
350          * flag to get own pinned shortcuts.
351          */
352         public static final int FLAG_MATCH_PINNED = 1 << 1;
353 
354         /** @hide kept for unit tests */
355         @Deprecated
356         public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
357 
358         /**
359          * Include manifest shortcuts in the result.
360          */
361         public static final int FLAG_MATCH_MANIFEST = 1 << 3;
362 
363         /**
364          * Include cached shortcuts in the result.
365          */
366         public static final int FLAG_MATCH_CACHED = 1 << 4;
367 
368         /** @hide kept for unit tests */
369         @Deprecated
370         public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
371 
372         /**
373          * Include all pinned shortcuts by any launchers, not just by the caller,
374          * in the result.
375          *
376          * <p>The caller must be the selected assistant app to use this flag, or have the system
377          * {@code ACCESS_SHORTCUTS} permission.
378          *
379          * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
380          * user owns on the launcher (or by other launchers, in case the user has multiple), use
381          * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
382          *
383          * <p>If you're a regular launcher app (or any app that's not the selected assistant app)
384          * then this flag will be ignored.
385          */
386         public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10;
387 
388         /**
389          * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED
390          * @hide
391          */
392         public static final int FLAG_MATCH_ALL_KINDS =
393                 FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_CACHED;
394 
395         /**
396          * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED
397          * @hide
398          */
399         public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED =
400                 FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
401 
402         /** @hide kept for unit tests */
403         @Deprecated
404         public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
405 
406         /**
407          * Requests "key" fields only.  See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to
408          * see which fields fields "key".
409          * This allows quicker access to shortcut information in order to
410          * determine whether the caller's in-memory cache needs to be updated.
411          *
412          * <p>Typically, launcher applications cache all or most shortcut information
413          * in memory in order to show shortcuts without a delay.
414          *
415          * When a given launcher application wants to update its cache, such as when its process
416          * restarts, it can fetch shortcut information with this flag.
417          * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
418          * shortcut, fetching a shortcut's non-key information only if that shortcut has been
419          * updated.
420          *
421          * @see ShortcutManager
422          */
423         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
424 
425         /** @hide */
426         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
427                 FLAG_MATCH_DYNAMIC,
428                 FLAG_MATCH_PINNED,
429                 FLAG_MATCH_MANIFEST,
430                 FLAG_MATCH_CACHED,
431                 FLAG_GET_KEY_FIELDS_ONLY,
432         })
433         @Retention(RetentionPolicy.SOURCE)
434         public @interface QueryFlags {}
435 
436         long mChangedSince;
437 
438         @Nullable
439         String mPackage;
440 
441         @Nullable
442         List<String> mShortcutIds;
443 
444         @Nullable
445         List<LocusId> mLocusIds;
446 
447         @Nullable
448         ComponentName mActivity;
449 
450         @QueryFlags
451         int mQueryFlags;
452 
ShortcutQuery()453         public ShortcutQuery() {
454         }
455 
456         /**
457          * If non-zero, returns only shortcuts that have been added or updated
458          * since the given timestamp, expressed in milliseconds since the Epoch&mdash;see
459          * {@link System#currentTimeMillis()}.
460          */
setChangedSince(long changedSince)461         public ShortcutQuery setChangedSince(long changedSince) {
462             mChangedSince = changedSince;
463             return this;
464         }
465 
466         /**
467          * If non-null, returns only shortcuts from the package.
468          */
setPackage(@ullable String packageName)469         public ShortcutQuery setPackage(@Nullable String packageName) {
470             mPackage = packageName;
471             return this;
472         }
473 
474         /**
475          * If non-null, return only the specified shortcuts by ID.  When setting this field,
476          * a package name must also be set with {@link #setPackage}.
477          */
setShortcutIds(@ullable List<String> shortcutIds)478         public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
479             mShortcutIds = shortcutIds;
480             return this;
481         }
482 
483         /**
484          * If non-null, return only the specified shortcuts by locus ID.  When setting this field,
485          * a package name must also be set with {@link #setPackage}.
486          */
487         @NonNull
setLocusIds(@ullable List<LocusId> locusIds)488         public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
489             mLocusIds = locusIds;
490             return this;
491         }
492 
493         /**
494          * If non-null, returns only shortcuts associated with the activity; i.e.
495          * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
496          * to {@code activity}.
497          */
setActivity(@ullable ComponentName activity)498         public ShortcutQuery setActivity(@Nullable ComponentName activity) {
499             mActivity = activity;
500             return this;
501         }
502 
503         /**
504          * Set query options.  At least one of the {@code MATCH} flags should be set.  Otherwise,
505          * no shortcuts will be returned.
506          *
507          * <ul>
508          *     <li>{@link #FLAG_MATCH_DYNAMIC}
509          *     <li>{@link #FLAG_MATCH_PINNED}
510          *     <li>{@link #FLAG_MATCH_MANIFEST}
511          *     <li>{@link #FLAG_MATCH_CACHED}
512          *     <li>{@link #FLAG_GET_KEY_FIELDS_ONLY}
513          * </ul>
514          */
setQueryFlags(@ueryFlags int queryFlags)515         public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
516             mQueryFlags = queryFlags;
517             return this;
518         }
519     }
520 
521     /**
522      * Callbacks for shortcut changes to this and related managed profiles.
523      *
524      * @hide
525      */
526     public interface ShortcutChangeCallback {
527         /**
528          * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
529          * register this callback, have been added or updated.
530          * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
531          * Executor)
532          *
533          * <p>Only the applications that are allowed to access the shortcut information,
534          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
535          *
536          * @param packageName The name of the package that has the shortcuts.
537          * @param shortcuts Shortcuts from the package that have updated or added. Only "key"
538          *    information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
539          * @param user The UserHandle of the profile that generated the change.
540          *
541          * @see ShortcutManager
542          */
onShortcutsAddedOrUpdated(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)543         default void onShortcutsAddedOrUpdated(@NonNull String packageName,
544                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
545 
546         /**
547          * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
548          * register this callback, have been removed.
549          * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
550          * Executor)
551          *
552          * <p>Only the applications that are allowed to access the shortcut information,
553          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
554          *
555          * @param packageName The name of the package that has the shortcuts.
556          * @param shortcuts Shortcuts from the package that have been removed. Only "key"
557          *    information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
558          * @param user The UserHandle of the profile that generated the change.
559          *
560          * @see ShortcutManager
561          */
onShortcutsRemoved(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)562         default void onShortcutsRemoved(@NonNull String packageName,
563                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
564     }
565 
566     /**
567      * Callback proxy class for {@link ShortcutChangeCallback}
568      *
569      * @hide
570      */
571     private static class ShortcutChangeCallbackProxy extends
572             android.content.pm.IShortcutChangeCallback.Stub {
573         private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences;
574 
ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback)575         ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) {
576             mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback));
577         }
578 
579         @Override
onShortcutsAddedOrUpdated(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)580         public void onShortcutsAddedOrUpdated(@NonNull String packageName,
581                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
582             Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
583             if (remoteReferences == null) {
584                 // Binder is dead.
585                 return;
586             }
587 
588             final Executor executor = remoteReferences.first;
589             final ShortcutChangeCallback callback = remoteReferences.second;
590             executor.execute(
591                     PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsAddedOrUpdated,
592                             callback, packageName, shortcuts, user).recycleOnUse());
593         }
594 
595         @Override
onShortcutsRemoved(@onNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user)596         public void onShortcutsRemoved(@NonNull String packageName,
597                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
598             Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
599             if (remoteReferences == null) {
600                 // Binder is dead.
601                 return;
602             }
603 
604             final Executor executor = remoteReferences.first;
605             final ShortcutChangeCallback callback = remoteReferences.second;
606             executor.execute(
607                     PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsRemoved,
608                             callback, packageName, shortcuts, user).recycleOnUse());
609         }
610     }
611 
612     /** @hide */
LauncherApps(Context context, ILauncherApps service)613     public LauncherApps(Context context, ILauncherApps service) {
614         mContext = context;
615         mService = service;
616         mPm = context.getPackageManager();
617         mUserManager = context.getSystemService(UserManager.class);
618     }
619 
620     /** @hide */
621     @TestApi
LauncherApps(Context context)622     public LauncherApps(Context context) {
623         this(context, ILauncherApps.Stub.asInterface(
624                 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
625     }
626 
627     /**
628      * Show an error log on logcat, when the calling user is a managed profile, the target
629      * user is different from the calling user, and it is not called from a package that has the
630      * {@link permission.INTERACT_ACROSS_USERS_FULL} permission, in order to help
631      * developers to detect it.
632      */
logErrorForInvalidProfileAccess(@onNull UserHandle target)633     private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) {
634         if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()
635                     && mContext.checkSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
636                             != PackageManager.PERMISSION_GRANTED) {
637             Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed.");
638         }
639     }
640 
641     /**
642      * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs.
643      *
644      * <p>If the caller is running on a managed profile, it'll return only the current profile.
645      * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
646      */
getProfiles()647     public List<UserHandle> getProfiles() {
648         if (mUserManager.isManagedProfile()) {
649             // If it's a managed profile, only return the current profile.
650             final List result =  new ArrayList(1);
651             result.add(android.os.Process.myUserHandle());
652             return result;
653         } else {
654             return mUserManager.getUserProfiles();
655         }
656     }
657 
658     /**
659      * Retrieves a list of activities that specify {@link Intent#ACTION_MAIN} and
660      * {@link Intent#CATEGORY_LAUNCHER}, across all apps, for a specified user. If an app doesn't
661      * have any activities that specify <code>ACTION_MAIN</code> or <code>CATEGORY_LAUNCHER</code>,
662      * the system adds a synthesized activity to the list. This synthesized activity represents the
663      * app's details page within system settings.
664      *
665      * <p class="note"><b>Note: </b>It's possible for system apps, such as app stores, to prevent
666      * the system from adding synthesized activities to the returned list.</p>
667      *
668      * <p>As of <a href="/reference/android/os/Build.VERSION_CODES.html#Q">Android Q</a>, at least
669      * one of the app's activities or synthesized activities appears in the returned list unless the
670      * app satisfies at least one of the following conditions:</p>
671      * <ul>
672      * <li>The app is a system app.</li>
673      * <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>.
674      * </li>
675      * <li>The app doesn't have a <em>launcher activity</em> that is enabled by default. A launcher
676      * activity has an intent containing the <code>ACTION_MAIN</code> action and the
677      * <code>CATEGORY_LAUNCHER</code> category.</li>
678      * </ul>
679      *
680      * <p>Additionally, the system hides synthesized activities for some or all apps in the
681      * following enterprise-related cases:</p>
682      * <ul>
683      * <li>If the device is a
684      * <a href="https://developers.google.com/android/work/overview#company-owned-devices-for-knowledge-workers">fully
685      * managed device</a>, no synthesized activities for any app appear in the returned list.</li>
686      * <li>If the current user has a
687      * <a href="https://developers.google.com/android/work/overview#employee-owned-devices-byod">work
688      * profile</a>, no synthesized activities for the user's work apps appear in the returned
689      * list.</li>
690      * </ul>
691      *
692      * @param packageName The specific package to query. If null, it checks all installed packages
693      *            in the profile.
694      * @param user The UserHandle of the profile.
695      * @return List of launchable activities. Can be an empty list but will not be null.
696      */
getActivityList(String packageName, UserHandle user)697     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
698         logErrorForInvalidProfileAccess(user);
699         try {
700             return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
701                     packageName, user), user);
702         } catch (RemoteException re) {
703             throw re.rethrowFromSystemServer();
704         }
705     }
706 
707     /**
708      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
709      * returns null.
710      *
711      * @param intent The intent to find a match for.
712      * @param user The profile to look in for a match.
713      * @return An activity info object if there is a match.
714      */
resolveActivity(Intent intent, UserHandle user)715     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
716         logErrorForInvalidProfileAccess(user);
717         try {
718             ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(),
719                     intent.getComponent(), user);
720             if (ai != null) {
721                 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
722                 return info;
723             }
724         } catch (RemoteException re) {
725             throw re.rethrowFromSystemServer();
726         }
727         return null;
728     }
729 
730     /**
731      * Starts a Main activity in the specified profile.
732      *
733      * @param component The ComponentName of the activity to launch
734      * @param user The UserHandle of the profile
735      * @param sourceBounds The Rect containing the source bounds of the clicked icon
736      * @param opts Options to pass to startActivity
737      */
startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)738     public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
739             Bundle opts) {
740         logErrorForInvalidProfileAccess(user);
741         if (DEBUG) {
742             Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
743         }
744         try {
745             mService.startActivityAsUser(mContext.getIApplicationThread(),
746                     mContext.getPackageName(), mContext.getAttributionTag(),
747                     component, sourceBounds, opts, user);
748         } catch (RemoteException re) {
749             throw re.rethrowFromSystemServer();
750         }
751     }
752 
753     /**
754      * Starts an activity to show the details of the specified session.
755      *
756      * @param sessionInfo The SessionInfo of the session
757      * @param sourceBounds The Rect containing the source bounds of the clicked icon
758      * @param opts Options to pass to startActivity
759      */
startPackageInstallerSessionDetailsActivity(@onNull SessionInfo sessionInfo, @Nullable Rect sourceBounds, @Nullable Bundle opts)760     public void startPackageInstallerSessionDetailsActivity(@NonNull SessionInfo sessionInfo,
761             @Nullable Rect sourceBounds, @Nullable Bundle opts) {
762         try {
763             mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
764                     mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo,
765                     sourceBounds, opts, sessionInfo.getUser());
766         } catch (RemoteException re) {
767             throw re.rethrowFromSystemServer();
768         }
769     }
770 
771     /**
772      * Starts the settings activity to show the application details for a
773      * package in the specified profile.
774      *
775      * @param component The ComponentName of the package to launch settings for.
776      * @param user The UserHandle of the profile
777      * @param sourceBounds The Rect containing the source bounds of the clicked icon
778      * @param opts Options to pass to startActivity
779      */
startAppDetailsActivity(ComponentName component, UserHandle user, Rect sourceBounds, Bundle opts)780     public void startAppDetailsActivity(ComponentName component, UserHandle user,
781             Rect sourceBounds, Bundle opts) {
782         logErrorForInvalidProfileAccess(user);
783         try {
784             mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
785                     mContext.getPackageName(), mContext.getAttributionTag(),
786                     component, sourceBounds, opts, user);
787         } catch (RemoteException re) {
788             throw re.rethrowFromSystemServer();
789         }
790     }
791 
792     /**
793      * Retrieves a list of config activities for creating {@link ShortcutInfo}.
794      *
795      * @param packageName The specific package to query. If null, it checks all installed packages
796      *            in the profile.
797      * @param user The UserHandle of the profile.
798      * @return List of config activities. Can be an empty list but will not be null.
799      *
800      * @see Intent#ACTION_CREATE_SHORTCUT
801      * @see #getShortcutConfigActivityIntent(LauncherActivityInfo)
802      */
getShortcutConfigActivityList(@ullable String packageName, @NonNull UserHandle user)803     public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName,
804             @NonNull UserHandle user) {
805         logErrorForInvalidProfileAccess(user);
806         try {
807             return convertToActivityList(mService.getShortcutConfigActivities(
808                     mContext.getPackageName(), packageName, user),
809                     user);
810         } catch (RemoteException re) {
811             throw re.rethrowFromSystemServer();
812         }
813     }
814 
convertToActivityList( @ullable ParceledListSlice<ResolveInfo> activities, UserHandle user)815     private List<LauncherActivityInfo> convertToActivityList(
816             @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
817         if (activities == null) {
818             return Collections.EMPTY_LIST;
819         }
820         ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
821         for (ResolveInfo ri : activities.getList()) {
822             LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
823             if (DEBUG) {
824                 Log.v(TAG, "Returning activity for profile " + user + " : "
825                         + lai.getComponentName());
826             }
827             lais.add(lai);
828         }
829         return lais;
830     }
831 
832     /**
833      * Returns an intent sender which can be used to start the configure activity for creating
834      * custom shortcuts. Use this method if the provider is in another profile as you are not
835      * allowed to start an activity in another profile.
836      *
837      * <p>The caller should receive {@link PinItemRequest} in onActivityResult on
838      * {@link android.app.Activity#RESULT_OK}.
839      *
840      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
841      * #hasShortcutHostPermission()}.
842      *
843      * @param info a configuration activity returned by {@link #getShortcutConfigActivityList}
844      *
845      * @throws IllegalStateException when the user is locked or not running.
846      * @throws SecurityException if {@link #hasShortcutHostPermission()} is false.
847      *
848      * @see #getPinItemRequest(Intent)
849      * @see Intent#ACTION_CREATE_SHORTCUT
850      * @see android.app.Activity#startIntentSenderForResult
851      */
852     @Nullable
getShortcutConfigActivityIntent(@onNull LauncherActivityInfo info)853     public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) {
854         try {
855             return mService.getShortcutConfigActivityIntent(
856                     mContext.getPackageName(), info.getComponentName(), info.getUser());
857         } catch (RemoteException re) {
858             throw re.rethrowFromSystemServer();
859         }
860     }
861 
862     /**
863      * Checks if the package is installed and enabled for a profile.
864      *
865      * @param packageName The package to check.
866      * @param user The UserHandle of the profile.
867      *
868      * @return true if the package exists and is enabled.
869      */
isPackageEnabled(String packageName, UserHandle user)870     public boolean isPackageEnabled(String packageName, UserHandle user) {
871         logErrorForInvalidProfileAccess(user);
872         try {
873             return mService.isPackageEnabled(mContext.getPackageName(), packageName, user);
874         } catch (RemoteException re) {
875             throw re.rethrowFromSystemServer();
876         }
877     }
878 
879     /**
880      * Gets the launcher extras supplied to the system when the given package was suspended via
881      * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
882      * PersistableBundle, String)}.
883      *
884      * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
885      * app and the launcher.
886      *
887      * <p>Note: This just returns whatever extras were provided to the system, <em>which might
888      * even be {@code null}.</em>
889      *
890      * @param packageName The package for which to fetch the launcher extras.
891      * @param user The {@link UserHandle} of the profile.
892      * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
893      *         suspended.
894      *
895      * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
896      * @see PackageManager#isPackageSuspended()
897      */
getSuspendedPackageLauncherExtras(String packageName, UserHandle user)898     public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
899         logErrorForInvalidProfileAccess(user);
900         try {
901             return mService.getSuspendedPackageLauncherExtras(packageName, user);
902         } catch (RemoteException re) {
903             throw re.rethrowFromSystemServer();
904         }
905     }
906 
907     /**
908      * Returns whether a package should be hidden from suggestions to the user. Currently, this
909      * could be done because the package was marked as distracting to the user via
910      * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
911      *
912      * @param packageName The package for which to check.
913      * @param user the {@link UserHandle} of the profile.
914      * @return
915      */
shouldHideFromSuggestions(@onNull String packageName, @NonNull UserHandle user)916     public boolean shouldHideFromSuggestions(@NonNull String packageName,
917             @NonNull UserHandle user) {
918         Objects.requireNonNull(packageName, "packageName");
919         Objects.requireNonNull(user, "user");
920         try {
921             return mService.shouldHideFromSuggestions(packageName, user);
922         } catch (RemoteException re) {
923             throw re.rethrowFromSystemServer();
924         }
925     }
926 
927     /**
928      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
929      *
930      * @param packageName The package name of the application
931      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
932      * @param user The UserHandle of the profile.
933      *
934      * @return {@link ApplicationInfo} containing information about the package. Returns
935      *         {@code null} if the package isn't installed for the given profile, or the profile
936      *         isn't enabled.
937      */
getApplicationInfo(@onNull String packageName, @ApplicationInfoFlags int flags, @NonNull UserHandle user)938     public ApplicationInfo getApplicationInfo(@NonNull String packageName,
939             @ApplicationInfoFlags int flags, @NonNull UserHandle user)
940             throws PackageManager.NameNotFoundException {
941         Objects.requireNonNull(packageName, "packageName");
942         Objects.requireNonNull(user, "user");
943         logErrorForInvalidProfileAccess(user);
944         try {
945             final ApplicationInfo ai = mService
946                     .getApplicationInfo(mContext.getPackageName(), packageName, flags, user);
947             if (ai == null) {
948                 throw new NameNotFoundException("Package " + packageName + " not found for user "
949                         + user.getIdentifier());
950             }
951             return ai;
952         } catch (RemoteException re) {
953             throw re.rethrowFromSystemServer();
954         }
955     }
956 
957     /**
958      * Returns an object describing the app usage limit for the given package.
959      * If there are multiple limits that apply to the package, the one with the smallest
960      * time remaining will be returned.
961      *
962      * @param packageName name of the package whose app usage limit will be returned
963      * @param user the user of the package
964      *
965      * @return an {@link AppUsageLimit} object describing the app time limit containing
966      * the given package with the smallest time remaining, or {@code null} if none exist.
967      * @throws SecurityException when the caller is not the recents app.
968      * @hide
969      */
970     @Nullable
971     @SystemApi
getAppUsageLimit(@onNull String packageName, @NonNull UserHandle user)972     public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName,
973             @NonNull UserHandle user) {
974         try {
975             return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user);
976         } catch (RemoteException re) {
977             throw re.rethrowFromSystemServer();
978         }
979     }
980 
981     /**
982      * Checks if the activity exists and it enabled for a profile.
983      *
984      * <p>The activity may still not be exported, in which case {@link #startMainActivity} will
985      * throw a {@link SecurityException} unless the caller has the same UID as the target app's.
986      *
987      * @param component The activity to check.
988      * @param user The UserHandle of the profile.
989      *
990      * @return true if the activity exists and is enabled.
991      */
isActivityEnabled(ComponentName component, UserHandle user)992     public boolean isActivityEnabled(ComponentName component, UserHandle user) {
993         logErrorForInvalidProfileAccess(user);
994         try {
995             return mService.isActivityEnabled(mContext.getPackageName(), component, user);
996         } catch (RemoteException re) {
997             throw re.rethrowFromSystemServer();
998         }
999     }
1000 
1001     /**
1002      * Returns whether the caller can access the shortcut information.  Access is currently
1003      * available to:
1004      *
1005      * <ul>
1006      *     <li>The current launcher (or default launcher if there is no set current launcher).</li>
1007      *     <li>The currently active voice interaction service.</li>
1008      * </ul>
1009      *
1010      * <p>Note when this method returns {@code false}, it may be a temporary situation because
1011      * the user is trying a new launcher application.  The user may decide to change the default
1012      * launcher back to the calling application again, so even if a launcher application loses
1013      * this permission, it does <b>not</b> have to purge pinned shortcut information.
1014      * If the calling launcher application contains pinned shortcuts, they will still work,
1015      * even though the caller no longer has the shortcut host permission.
1016      *
1017      * @throws IllegalStateException when the user is locked.
1018      *
1019      * @see ShortcutManager
1020      */
hasShortcutHostPermission()1021     public boolean hasShortcutHostPermission() {
1022         try {
1023             return mService.hasShortcutHostPermission(mContext.getPackageName());
1024         } catch (RemoteException re) {
1025             throw re.rethrowFromSystemServer();
1026         }
1027     }
1028 
maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts)1029     private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) {
1030         if (shortcuts == null) {
1031             return null;
1032         }
1033         for (int i = shortcuts.size() - 1; i >= 0; i--) {
1034             final ShortcutInfo si = shortcuts.get(i);
1035             final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext,
1036                     si.getDisabledReason());
1037             if (message != null) {
1038                 si.setDisabledMessage(message);
1039             }
1040         }
1041         return shortcuts;
1042     }
1043 
1044     /**
1045      * Returns {@link ShortcutInfo}s that match {@code query}.
1046      *
1047      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
1048      * #hasShortcutHostPermission()}.
1049      *
1050      * @param query result includes shortcuts matching this query.
1051      * @param user The UserHandle of the profile.
1052      *
1053      * @return the IDs of {@link ShortcutInfo}s that match the query.
1054      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1055      * is locked or not running.
1056      *
1057      * @see ShortcutManager
1058      */
1059     @Nullable
getShortcuts(@onNull ShortcutQuery query, @NonNull UserHandle user)1060     public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
1061             @NonNull UserHandle user) {
1062         logErrorForInvalidProfileAccess(user);
1063         try {
1064             // Note this is the only case we need to update the disabled message for shortcuts
1065             // that weren't restored.
1066             // The restore problem messages are only shown by the user, and publishers will never
1067             // see them. The only other API that the launcher gets shortcuts is the shortcut
1068             // changed callback, but that only returns shortcuts with the "key" information, so
1069             // that won't return disabled message.
1070             return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
1071                     new ShortcutQueryWrapper(query), user)
1072                     .getList());
1073         } catch (RemoteException e) {
1074             throw e.rethrowFromSystemServer();
1075         }
1076     }
1077 
1078     /**
1079      * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
1080      */
1081     @Nullable
1082     @Deprecated
getShortcutInfo(@onNull String packageName, @NonNull List<String> ids, @NonNull UserHandle user)1083     public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
1084             @NonNull List<String> ids, @NonNull UserHandle user) {
1085         final ShortcutQuery q = new ShortcutQuery();
1086         q.setPackage(packageName);
1087         q.setShortcutIds(ids);
1088         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
1089         return getShortcuts(q, user);
1090     }
1091 
1092     /**
1093      * Pin shortcuts on a package.
1094      *
1095      * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
1096      * However, different launchers may have different set of pinned shortcuts.
1097      *
1098      * <p>The calling launcher application must be allowed to access the shortcut information,
1099      * as defined in {@link #hasShortcutHostPermission()}.
1100      *
1101      * @param packageName The target package name.
1102      * @param shortcutIds The IDs of the shortcut to be pinned.
1103      * @param user The UserHandle of the profile.
1104      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1105      * is locked or not running.
1106      *
1107      * @see ShortcutManager
1108      */
pinShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user)1109     public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
1110             @NonNull UserHandle user) {
1111         logErrorForInvalidProfileAccess(user);
1112         try {
1113             mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
1114         } catch (RemoteException e) {
1115             throw e.rethrowFromSystemServer();
1116         }
1117     }
1118 
1119     /**
1120      * Mark shortcuts as cached for a package.
1121      *
1122      * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts
1123      * in the list will be ignored.
1124      *
1125      * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned
1126      * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same
1127      * shortcut, it can be uncached by any valid caller.
1128      *
1129      * @param packageName The target package name.
1130      * @param shortcutIds The IDs of the shortcut to be cached.
1131      * @param user The UserHandle of the profile.
1132      * @param cacheFlags One of the values in:
1133      * <ul>
1134      *     <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS}
1135      *     <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS}
1136      * </ul>
1137      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1138      * is locked or not running.
1139      *
1140      * @see ShortcutManager
1141      *
1142      * @hide
1143      */
1144     @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
cacheShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags)1145     public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
1146             @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags) {
1147         logErrorForInvalidProfileAccess(user);
1148         try {
1149             mService.cacheShortcuts(
1150                     mContext.getPackageName(), packageName, shortcutIds, user, cacheFlags);
1151         } catch (RemoteException e) {
1152             throw e.rethrowFromSystemServer();
1153         }
1154     }
1155 
1156     /**
1157      * Remove cached flag from shortcuts for a package.
1158      *
1159      * @param packageName The target package name.
1160      * @param shortcutIds The IDs of the shortcut to be uncached.
1161      * @param user The UserHandle of the profile.
1162      * @param cacheFlags One of the values in:
1163      * <ul>
1164      *     <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS}
1165      *     <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS}
1166      * </ul>
1167      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1168      * is locked or not running.
1169      *
1170      * @see ShortcutManager
1171      *
1172      * @hide
1173      */
1174     @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
uncacheShortcuts(@onNull String packageName, @NonNull List<String> shortcutIds, @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags)1175     public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
1176             @NonNull UserHandle user, @ShortcutCacheFlags int cacheFlags) {
1177         logErrorForInvalidProfileAccess(user);
1178         try {
1179             mService.uncacheShortcuts(
1180                     mContext.getPackageName(), packageName, shortcutIds, user, cacheFlags);
1181         } catch (RemoteException e) {
1182             throw e.rethrowFromSystemServer();
1183         }
1184     }
1185 
1186     /**
1187      * @hide kept for testing.
1188      */
1189     @Deprecated
getShortcutIconResId(@onNull ShortcutInfo shortcut)1190     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
1191         return shortcut.getIconResourceId();
1192     }
1193 
1194     /**
1195      * @hide kept for testing.
1196      */
1197     @Deprecated
getShortcutIconResId(@onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)1198     public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
1199             @NonNull UserHandle user) {
1200         final ShortcutQuery q = new ShortcutQuery();
1201         q.setPackage(packageName);
1202         q.setShortcutIds(Arrays.asList(shortcutId));
1203         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
1204         final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
1205 
1206         return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
1207     }
1208 
1209     /**
1210      * @hide internal/unit tests only
1211      */
getShortcutIconFd( @onNull ShortcutInfo shortcut)1212     public ParcelFileDescriptor getShortcutIconFd(
1213             @NonNull ShortcutInfo shortcut) {
1214         return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
1215                 shortcut.getUserId());
1216     }
1217 
1218     /**
1219      * @hide internal/unit tests only
1220      */
getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user)1221     public ParcelFileDescriptor getShortcutIconFd(
1222             @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
1223         return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
1224     }
1225 
getShortcutIconFd( @onNull String packageName, @NonNull String shortcutId, int userId)1226     private ParcelFileDescriptor getShortcutIconFd(
1227             @NonNull String packageName, @NonNull String shortcutId, int userId) {
1228         try {
1229             return mService.getShortcutIconFd(mContext.getPackageName(),
1230                     packageName, shortcutId, userId);
1231         } catch (RemoteException e) {
1232             throw e.rethrowFromSystemServer();
1233         }
1234     }
1235 
1236     /**
1237      * @hide internal/unit tests only
1238      */
1239     @VisibleForTesting
getUriShortcutIconFd(@onNull ShortcutInfo shortcut)1240     public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) {
1241         return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId());
1242     }
1243 
getUriShortcutIconFd(@onNull String packageName, @NonNull String shortcutId, int userId)1244     private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
1245             @NonNull String shortcutId, int userId) {
1246         String uri = getShortcutIconUri(packageName, shortcutId, userId);
1247         if (uri == null) {
1248             return null;
1249         }
1250         try {
1251             return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
1252         } catch (FileNotFoundException e) {
1253             Log.e(TAG, "Icon file not found: " + uri);
1254             return null;
1255         }
1256     }
1257 
getShortcutIconUri(@onNull String packageName, @NonNull String shortcutId, int userId)1258     private String getShortcutIconUri(@NonNull String packageName,
1259             @NonNull String shortcutId, int userId) {
1260         String uri = null;
1261         try {
1262             uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
1263                     userId);
1264         } catch (RemoteException e) {
1265             throw e.rethrowFromSystemServer();
1266         }
1267         return uri;
1268     }
1269 
1270     /**
1271      * Returns the icon for this shortcut, without any badging for the profile.
1272      *
1273      * <p>The calling launcher application must be allowed to access the shortcut information,
1274      * as defined in {@link #hasShortcutHostPermission()}.
1275      *
1276      * @param density The preferred density of the icon, zero for default density. Use
1277      * density DPI values from {@link DisplayMetrics}.
1278      *
1279      * @return The drawable associated with the shortcut.
1280      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1281      * is locked or not running.
1282      *
1283      * @see ShortcutManager
1284      * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
1285      * @see DisplayMetrics
1286      */
getShortcutIconDrawable(@onNull ShortcutInfo shortcut, int density)1287     public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
1288         if (shortcut.hasIconFile()) {
1289             final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
1290             return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
1291         } else if (shortcut.hasIconUri()) {
1292             final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut);
1293             return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
1294         } else if (shortcut.hasIconResource()) {
1295             return loadDrawableResourceFromPackage(shortcut.getPackage(),
1296                     shortcut.getIconResourceId(), shortcut.getUserHandle(), density);
1297         } else if (shortcut.getIcon() != null) {
1298             // This happens if a shortcut is pending-approval.
1299             final Icon icon = shortcut.getIcon();
1300             switch (icon.getType()) {
1301                 case Icon.TYPE_RESOURCE: {
1302                     return loadDrawableResourceFromPackage(shortcut.getPackage(),
1303                             icon.getResId(), shortcut.getUserHandle(), density);
1304                 }
1305                 case Icon.TYPE_BITMAP:
1306                 case Icon.TYPE_ADAPTIVE_BITMAP: {
1307                     return icon.loadDrawable(mContext);
1308                 }
1309                 default:
1310                     return null; // Shouldn't happen though.
1311             }
1312         } else {
1313             return null; // Has no icon.
1314         }
1315     }
1316 
loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive)1317     private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) {
1318         if (pfd == null) {
1319             return null;
1320         }
1321         try {
1322             final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
1323             if (bmp != null) {
1324                 BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
1325                 if (adaptive) {
1326                     return new AdaptiveIconDrawable(null, dr);
1327                 } else {
1328                     return dr;
1329                 }
1330             }
1331             return null;
1332         } finally {
1333             try {
1334                 pfd.close();
1335             } catch (IOException ignore) {
1336             }
1337         }
1338     }
1339 
1340     /**
1341      * @hide
1342      */
getShortcutIcon(@onNull ShortcutInfo shortcut)1343     public Icon getShortcutIcon(@NonNull ShortcutInfo shortcut) {
1344         if (shortcut.hasIconFile()) {
1345             final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
1346             if (pfd == null) {
1347                 return null;
1348             }
1349             try {
1350                 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
1351                 if (bmp != null) {
1352                     if (shortcut.hasAdaptiveBitmap()) {
1353                         return Icon.createWithAdaptiveBitmap(bmp);
1354                     } else {
1355                         return Icon.createWithBitmap(bmp);
1356                     }
1357                 }
1358                 return null;
1359             } finally {
1360                 try {
1361                     pfd.close();
1362                 } catch (IOException ignore) {
1363                 }
1364             }
1365         } else if (shortcut.hasIconUri()) {
1366             String uri = getShortcutIconUri(shortcut.getPackage(), shortcut.getId(),
1367                     shortcut.getUserId());
1368             if (uri == null) {
1369                 return null;
1370             }
1371             if (shortcut.hasAdaptiveBitmap()) {
1372                 return Icon.createWithAdaptiveBitmapContentUri(uri);
1373             } else {
1374                 return Icon.createWithContentUri(uri);
1375             }
1376         } else if (shortcut.hasIconResource()) {
1377             return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId());
1378         } else {
1379             return shortcut.getIcon();
1380         }
1381     }
1382 
loadDrawableResourceFromPackage(String packageName, int resId, UserHandle user, int density)1383     private Drawable loadDrawableResourceFromPackage(String packageName, int resId,
1384             UserHandle user, int density) {
1385         try {
1386             if (resId == 0) {
1387                 return null; // Shouldn't happen but just in case.
1388             }
1389             final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user);
1390             final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
1391             return res.getDrawableForDensity(resId, density);
1392         } catch (NameNotFoundException | Resources.NotFoundException e) {
1393             return null;
1394         }
1395     }
1396 
1397     /**
1398      * Returns the shortcut icon with badging appropriate for the profile.
1399      *
1400      * <p>The calling launcher application must be allowed to access the shortcut information,
1401      * as defined in {@link #hasShortcutHostPermission()}.
1402      *
1403      * @param density Optional density for the icon, or 0 to use the default density. Use
1404      * @return A badged icon for the shortcut.
1405      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1406      * is locked or not running.
1407      *
1408      * @see ShortcutManager
1409      * @see #getShortcutIconDrawable(ShortcutInfo, int)
1410      * @see DisplayMetrics
1411      */
getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density)1412     public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
1413         final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
1414 
1415         return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
1416                 originalIcon, shortcut.getUserHandle());
1417     }
1418 
1419     /**
1420      * Starts a shortcut.
1421      *
1422      * <p>The calling launcher application must be allowed to access the shortcut information,
1423      * as defined in {@link #hasShortcutHostPermission()}.
1424      *
1425      * @param packageName The target shortcut package name.
1426      * @param shortcutId The target shortcut ID.
1427      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
1428      * @param startActivityOptions Options to pass to startActivity.
1429      * @param user The UserHandle of the profile.
1430      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1431      * is locked or not running.
1432      *
1433      * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
1434      * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
1435      */
startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user)1436     public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
1437             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
1438             @NonNull UserHandle user) {
1439         logErrorForInvalidProfileAccess(user);
1440 
1441         startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
1442                 user.getIdentifier());
1443     }
1444 
1445     /**
1446      * Launches a shortcut.
1447      *
1448      * <p>The calling launcher application must be allowed to access the shortcut information,
1449      * as defined in {@link #hasShortcutHostPermission()}.
1450      *
1451      * @param shortcut The target shortcut.
1452      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
1453      * @param startActivityOptions Options to pass to startActivity.
1454      * @throws IllegalStateException when the user is locked, or when the {@code user} user
1455      * is locked or not running.
1456      *
1457      * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
1458      * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
1459      */
startShortcut(@onNull ShortcutInfo shortcut, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions)1460     public void startShortcut(@NonNull ShortcutInfo shortcut,
1461             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
1462         startShortcut(shortcut.getPackage(), shortcut.getId(),
1463                 sourceBounds, startActivityOptions,
1464                 shortcut.getUserId());
1465     }
1466 
1467     @UnsupportedAppUsage
startShortcut(@onNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, int userId)1468     private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
1469             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
1470             int userId) {
1471         try {
1472             final boolean success = mService.startShortcut(mContext.getPackageName(), packageName,
1473                     null /* default featureId */, shortcutId, sourceBounds, startActivityOptions,
1474                     userId);
1475             if (!success) {
1476                 throw new ActivityNotFoundException("Shortcut could not be started");
1477             }
1478         } catch (RemoteException e) {
1479             throw e.rethrowFromSystemServer();
1480         }
1481     }
1482 
1483     /**
1484      * Registers a callback for changes to packages in this user and managed profiles.
1485      *
1486      * @param callback The callback to register.
1487      */
registerCallback(Callback callback)1488     public void registerCallback(Callback callback) {
1489         registerCallback(callback, null);
1490     }
1491 
1492     /**
1493      * Registers a callback for changes to packages in this user and managed profiles.
1494      *
1495      * @param callback The callback to register.
1496      * @param handler that should be used to post callbacks on, may be null.
1497      */
registerCallback(Callback callback, Handler handler)1498     public void registerCallback(Callback callback, Handler handler) {
1499         synchronized (this) {
1500             if (callback != null && findCallbackLocked(callback) < 0) {
1501                 boolean addedFirstCallback = mCallbacks.size() == 0;
1502                 addCallbackLocked(callback, handler);
1503                 if (addedFirstCallback) {
1504                     try {
1505                         mService.addOnAppsChangedListener(mContext.getPackageName(),
1506                                 mAppsChangedListener);
1507                     } catch (RemoteException re) {
1508                         throw re.rethrowFromSystemServer();
1509                     }
1510                 }
1511             }
1512         }
1513     }
1514 
1515     /**
1516      * Unregisters a callback that was previously registered.
1517      *
1518      * @param callback The callback to unregister.
1519      * @see #registerCallback(Callback)
1520      */
unregisterCallback(Callback callback)1521     public void unregisterCallback(Callback callback) {
1522         synchronized (this) {
1523             removeCallbackLocked(callback);
1524             if (mCallbacks.size() == 0) {
1525                 try {
1526                     mService.removeOnAppsChangedListener(mAppsChangedListener);
1527                 } catch (RemoteException re) {
1528                     throw re.rethrowFromSystemServer();
1529                 }
1530             }
1531         }
1532     }
1533 
1534     /** @return position in mCallbacks for callback or -1 if not present. */
findCallbackLocked(Callback callback)1535     private int findCallbackLocked(Callback callback) {
1536         if (callback == null) {
1537             throw new IllegalArgumentException("Callback cannot be null");
1538         }
1539         final int size = mCallbacks.size();
1540         for (int i = 0; i < size; ++i) {
1541             if (mCallbacks.get(i).mCallback == callback) {
1542                 return i;
1543             }
1544         }
1545         return -1;
1546     }
1547 
removeCallbackLocked(Callback callback)1548     private void removeCallbackLocked(Callback callback) {
1549         int pos = findCallbackLocked(callback);
1550         if (pos >= 0) {
1551             mCallbacks.remove(pos);
1552         }
1553     }
1554 
addCallbackLocked(Callback callback, Handler handler)1555     private void addCallbackLocked(Callback callback, Handler handler) {
1556         // Remove if already present.
1557         removeCallbackLocked(callback);
1558         if (handler == null) {
1559             handler = new Handler();
1560         }
1561         CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
1562         mCallbacks.add(toAdd);
1563     }
1564 
1565     private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
1566 
1567         @Override
1568         public void onPackageRemoved(UserHandle user, String packageName)
1569                 throws RemoteException {
1570             if (DEBUG) {
1571                 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
1572             }
1573             synchronized (LauncherApps.this) {
1574                 for (CallbackMessageHandler callback : mCallbacks) {
1575                     callback.postOnPackageRemoved(packageName, user);
1576                 }
1577             }
1578         }
1579 
1580         @Override
1581         public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
1582             if (DEBUG) {
1583                 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
1584             }
1585             synchronized (LauncherApps.this) {
1586                 for (CallbackMessageHandler callback : mCallbacks) {
1587                     callback.postOnPackageChanged(packageName, user);
1588                 }
1589             }
1590         }
1591 
1592         @Override
1593         public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
1594             if (DEBUG) {
1595                 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
1596             }
1597             synchronized (LauncherApps.this) {
1598                 for (CallbackMessageHandler callback : mCallbacks) {
1599                     callback.postOnPackageAdded(packageName, user);
1600                 }
1601             }
1602         }
1603 
1604         @Override
1605         public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
1606                 throws RemoteException {
1607             if (DEBUG) {
1608                 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
1609             }
1610             synchronized (LauncherApps.this) {
1611                 for (CallbackMessageHandler callback : mCallbacks) {
1612                     callback.postOnPackagesAvailable(packageNames, user, replacing);
1613                 }
1614             }
1615         }
1616 
1617         @Override
1618         public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
1619                 throws RemoteException {
1620             if (DEBUG) {
1621                 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
1622             }
1623             synchronized (LauncherApps.this) {
1624                 for (CallbackMessageHandler callback : mCallbacks) {
1625                     callback.postOnPackagesUnavailable(packageNames, user, replacing);
1626                 }
1627             }
1628         }
1629 
1630         @Override
1631         public void onPackagesSuspended(UserHandle user, String[] packageNames,
1632                 Bundle launcherExtras)
1633                 throws RemoteException {
1634             if (DEBUG) {
1635                 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
1636             }
1637             synchronized (LauncherApps.this) {
1638                 for (CallbackMessageHandler callback : mCallbacks) {
1639                     callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
1640                 }
1641             }
1642         }
1643 
1644         @Override
1645         public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
1646                 throws RemoteException {
1647             if (DEBUG) {
1648                 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
1649             }
1650             synchronized (LauncherApps.this) {
1651                 for (CallbackMessageHandler callback : mCallbacks) {
1652                     callback.postOnPackagesUnsuspended(packageNames, user);
1653                 }
1654             }
1655         }
1656 
1657         @Override
1658         public void onShortcutChanged(UserHandle user, String packageName,
1659                 ParceledListSlice shortcuts) {
1660             if (DEBUG) {
1661                 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
1662             }
1663             final List<ShortcutInfo> list = shortcuts.getList();
1664             synchronized (LauncherApps.this) {
1665                 for (CallbackMessageHandler callback : mCallbacks) {
1666                     callback.postOnShortcutChanged(packageName, user, list);
1667                 }
1668             }
1669         }
1670     };
1671 
1672     private static class CallbackMessageHandler extends Handler {
1673         private static final int MSG_ADDED = 1;
1674         private static final int MSG_REMOVED = 2;
1675         private static final int MSG_CHANGED = 3;
1676         private static final int MSG_AVAILABLE = 4;
1677         private static final int MSG_UNAVAILABLE = 5;
1678         private static final int MSG_SUSPENDED = 6;
1679         private static final int MSG_UNSUSPENDED = 7;
1680         private static final int MSG_SHORTCUT_CHANGED = 8;
1681 
1682         private LauncherApps.Callback mCallback;
1683 
1684         private static class CallbackInfo {
1685             String[] packageNames;
1686             String packageName;
1687             Bundle launcherExtras;
1688             boolean replacing;
1689             UserHandle user;
1690             List<ShortcutInfo> shortcuts;
1691         }
1692 
CallbackMessageHandler(Looper looper, LauncherApps.Callback callback)1693         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
1694             super(looper, null, true);
1695             mCallback = callback;
1696         }
1697 
1698         @Override
handleMessage(Message msg)1699         public void handleMessage(Message msg) {
1700             if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
1701                 return;
1702             }
1703             CallbackInfo info = (CallbackInfo) msg.obj;
1704             switch (msg.what) {
1705                 case MSG_ADDED:
1706                     mCallback.onPackageAdded(info.packageName, info.user);
1707                     break;
1708                 case MSG_REMOVED:
1709                     mCallback.onPackageRemoved(info.packageName, info.user);
1710                     break;
1711                 case MSG_CHANGED:
1712                     mCallback.onPackageChanged(info.packageName, info.user);
1713                     break;
1714                 case MSG_AVAILABLE:
1715                     mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
1716                     break;
1717                 case MSG_UNAVAILABLE:
1718                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
1719                     break;
1720                 case MSG_SUSPENDED:
1721                     mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
1722                     );
1723                     break;
1724                 case MSG_UNSUSPENDED:
1725                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
1726                     break;
1727                 case MSG_SHORTCUT_CHANGED:
1728                     mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
1729                     break;
1730             }
1731         }
1732 
postOnPackageAdded(String packageName, UserHandle user)1733         public void postOnPackageAdded(String packageName, UserHandle user) {
1734             CallbackInfo info = new CallbackInfo();
1735             info.packageName = packageName;
1736             info.user = user;
1737             obtainMessage(MSG_ADDED, info).sendToTarget();
1738         }
1739 
postOnPackageRemoved(String packageName, UserHandle user)1740         public void postOnPackageRemoved(String packageName, UserHandle user) {
1741             CallbackInfo info = new CallbackInfo();
1742             info.packageName = packageName;
1743             info.user = user;
1744             obtainMessage(MSG_REMOVED, info).sendToTarget();
1745         }
1746 
postOnPackageChanged(String packageName, UserHandle user)1747         public void postOnPackageChanged(String packageName, UserHandle user) {
1748             CallbackInfo info = new CallbackInfo();
1749             info.packageName = packageName;
1750             info.user = user;
1751             obtainMessage(MSG_CHANGED, info).sendToTarget();
1752         }
1753 
postOnPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)1754         public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
1755                 boolean replacing) {
1756             CallbackInfo info = new CallbackInfo();
1757             info.packageNames = packageNames;
1758             info.replacing = replacing;
1759             info.user = user;
1760             obtainMessage(MSG_AVAILABLE, info).sendToTarget();
1761         }
1762 
postOnPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)1763         public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
1764                 boolean replacing) {
1765             CallbackInfo info = new CallbackInfo();
1766             info.packageNames = packageNames;
1767             info.replacing = replacing;
1768             info.user = user;
1769             obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
1770         }
1771 
postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, UserHandle user)1772         public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
1773                 UserHandle user) {
1774             CallbackInfo info = new CallbackInfo();
1775             info.packageNames = packageNames;
1776             info.user = user;
1777             info.launcherExtras = launcherExtras;
1778             obtainMessage(MSG_SUSPENDED, info).sendToTarget();
1779         }
1780 
postOnPackagesUnsuspended(String[] packageNames, UserHandle user)1781         public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
1782             CallbackInfo info = new CallbackInfo();
1783             info.packageNames = packageNames;
1784             info.user = user;
1785             obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
1786         }
1787 
postOnShortcutChanged(String packageName, UserHandle user, List<ShortcutInfo> shortcuts)1788         public void postOnShortcutChanged(String packageName, UserHandle user,
1789                 List<ShortcutInfo> shortcuts) {
1790             CallbackInfo info = new CallbackInfo();
1791             info.packageName = packageName;
1792             info.user = user;
1793             info.shortcuts = shortcuts;
1794             obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
1795         }
1796     }
1797 
1798     /**
1799      * Register a callback to watch for session lifecycle events in this user and managed profiles.
1800      * @param callback The callback to register.
1801      * @param executor {@link Executor} to handle the callbacks, cannot be null.
1802      *
1803      * @see PackageInstaller#registerSessionCallback(SessionCallback)
1804      */
registerPackageInstallerSessionCallback( @onNull @allbackExecutor Executor executor, @NonNull SessionCallback callback)1805     public void registerPackageInstallerSessionCallback(
1806             @NonNull @CallbackExecutor Executor executor, @NonNull SessionCallback callback) {
1807         if (executor == null) {
1808             throw new NullPointerException("Executor must not be null");
1809         }
1810 
1811         synchronized (mDelegates) {
1812             final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
1813                     executor);
1814             try {
1815                 mService.registerPackageInstallerCallback(mContext.getPackageName(),
1816                         delegate);
1817             } catch (RemoteException e) {
1818                 throw e.rethrowFromSystemServer();
1819             }
1820             mDelegates.add(delegate);
1821         }
1822     }
1823 
1824     /**
1825      * Unregisters a callback that was previously registered.
1826      *
1827      * @param callback The callback to unregister.
1828      * @see #registerPackageInstallerSessionCallback(Executor, SessionCallback)
1829      */
unregisterPackageInstallerSessionCallback(@onNull SessionCallback callback)1830     public void unregisterPackageInstallerSessionCallback(@NonNull SessionCallback callback) {
1831         synchronized (mDelegates) {
1832             for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
1833                 final SessionCallbackDelegate delegate = i.next();
1834                 if (delegate.mCallback == callback) {
1835                     mPm.getPackageInstaller().unregisterSessionCallback(delegate.mCallback);
1836                     i.remove();
1837                 }
1838             }
1839         }
1840     }
1841 
1842     /**
1843      * Return list of all known install sessions in this user and managed profiles, regardless
1844      * of the installer.
1845      *
1846      * @see PackageInstaller#getAllSessions()
1847      */
getAllPackageInstallerSessions()1848     public @NonNull List<SessionInfo> getAllPackageInstallerSessions() {
1849         try {
1850             return mService.getAllSessions(mContext.getPackageName()).getList();
1851         } catch (RemoteException e) {
1852             throw e.rethrowFromSystemServer();
1853         }
1854     }
1855 
1856     /**
1857      * Register a callback to watch for shortcut change events in this user and managed profiles.
1858      *
1859      * @param callback The callback to register.
1860      * @param query {@link ShortcutQuery} to match and filter the shortcut events. Only matching
1861      * shortcuts will be returned by the callback.
1862      * @param executor {@link Executor} to handle the callbacks. To dispatch callbacks to the main
1863      * thread of your application, you can use {@link android.content.Context#getMainExecutor()}.
1864      *
1865      * @hide
1866      */
registerShortcutChangeCallback(@onNull ShortcutChangeCallback callback, @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor)1867     public void registerShortcutChangeCallback(@NonNull ShortcutChangeCallback callback,
1868             @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor) {
1869         Objects.requireNonNull(callback, "Callback cannot be null");
1870         Objects.requireNonNull(query, "Query cannot be null");
1871         Objects.requireNonNull(executor, "Executor cannot be null");
1872 
1873         synchronized (mShortcutChangeCallbacks) {
1874             IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback);
1875             mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy));
1876             try {
1877                 mService.registerShortcutChangeCallback(mContext.getPackageName(),
1878                         new ShortcutQueryWrapper(query), proxy);
1879             } catch (RemoteException e) {
1880                 throw e.rethrowFromSystemServer();
1881             }
1882         }
1883     }
1884 
1885     /**
1886      * Unregisters a callback that was previously registered.
1887      * @see #registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, Executor)
1888      *
1889      * @param callback Callback to be unregistered.
1890      *
1891      * @hide
1892      */
unregisterShortcutChangeCallback(@onNull ShortcutChangeCallback callback)1893     public void unregisterShortcutChangeCallback(@NonNull ShortcutChangeCallback callback) {
1894         Objects.requireNonNull(callback, "Callback cannot be null");
1895 
1896         synchronized (mShortcutChangeCallbacks) {
1897             if (mShortcutChangeCallbacks.containsKey(callback)) {
1898                 IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second;
1899                 try {
1900                     mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy);
1901                 } catch (RemoteException e) {
1902                     throw e.rethrowFromSystemServer();
1903                 }
1904             }
1905         }
1906     }
1907 
1908     /**
1909      * A helper method to extract a {@link PinItemRequest} set to
1910      * the {@link #EXTRA_PIN_ITEM_REQUEST} extra.
1911      */
getPinItemRequest(Intent intent)1912     public PinItemRequest getPinItemRequest(Intent intent) {
1913         return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
1914     }
1915 
1916     /**
1917      * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with
1918      * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent
1919      * respectively to the default launcher app.
1920      *
1921      * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type.</h3>
1922      *
1923      * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
1924      * {@link ShortcutInfo}.  If the launcher accepts a request, call {@link #accept()},
1925      * or {@link #accept(Bundle)} with a null or empty Bundle.  No options are defined for
1926      * pin-shortcuts requests.
1927      *
1928      * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type.
1929      *
1930      * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in
1931      * which case {@link ShortcutInfo#isPinned()} returns true.  This means the user wants to create
1932      * another pinned shortcut for a shortcut that's already pinned.  If the launcher accepts it,
1933      * {@link #accept()} must still be called even though the shortcut is already pinned, and
1934      * create a new pinned shortcut icon for it.
1935      *
1936      * <p>See also {@link ShortcutManager} for more details.
1937      *
1938      * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type.</h3>
1939      *
1940      * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
1941      * an AppWidget.  If the launcher accepts a request, call {@link #accept(Bundle)} with
1942      * the appwidget integer ID set to the
1943      * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra.
1944      *
1945      * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null
1946      * {@link AppWidgetProviderInfo} for this type.
1947      *
1948      * <p>See also {@link AppWidgetManager} for more details.
1949      *
1950      * @see #EXTRA_PIN_ITEM_REQUEST
1951      * @see #getPinItemRequest(Intent)
1952      */
1953     public static final class PinItemRequest implements Parcelable {
1954 
1955         /** This is a request to pin shortcut. */
1956         public static final int REQUEST_TYPE_SHORTCUT = 1;
1957 
1958         /** This is a request to pin app widget. */
1959         public static final int REQUEST_TYPE_APPWIDGET = 2;
1960 
1961         /** @hide */
1962         @IntDef(prefix = { "REQUEST_TYPE_" }, value = {
1963                 REQUEST_TYPE_SHORTCUT,
1964                 REQUEST_TYPE_APPWIDGET
1965         })
1966         @Retention(RetentionPolicy.SOURCE)
1967         public @interface RequestType {}
1968 
1969         private final int mRequestType;
1970         private final IPinItemRequest mInner;
1971 
1972         /**
1973          * @hide
1974          */
PinItemRequest(IPinItemRequest inner, int type)1975         public PinItemRequest(IPinItemRequest inner, int type) {
1976             mInner = inner;
1977             mRequestType = type;
1978         }
1979 
1980         /**
1981          * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants.
1982          *
1983          * @return one of the {@code REQUEST_TYPE_} constants.
1984          */
1985         @RequestType
getRequestType()1986         public int getRequestType() {
1987             return mRequestType;
1988         }
1989 
1990         /**
1991          * {@link ShortcutInfo} sent by the requesting app.
1992          * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a
1993          * different request type.
1994          *
1995          * @return requested {@link ShortcutInfo} when a request is of the
1996          * {@link #REQUEST_TYPE_SHORTCUT} type.  Null otherwise.
1997          */
1998         @Nullable
getShortcutInfo()1999         public ShortcutInfo getShortcutInfo() {
2000             try {
2001                 return mInner.getShortcutInfo();
2002             } catch (RemoteException e) {
2003                 throw e.rethrowAsRuntimeException();
2004             }
2005         }
2006 
2007         /**
2008          * {@link AppWidgetProviderInfo} sent by the requesting app.
2009          * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
2010          * different request type.
2011          *
2012          * <p>Launcher should not show any configuration activity associated with the provider, and
2013          * assume that the widget is already fully configured. Upon accepting the widget, it should
2014          * pass the widgetId in {@link #accept(Bundle)}.
2015          *
2016          * @return requested {@link AppWidgetProviderInfo} when a request is of the
2017          * {@link #REQUEST_TYPE_APPWIDGET} type.  Null otherwise.
2018          */
2019         @Nullable
getAppWidgetProviderInfo(Context context)2020         public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
2021             try {
2022                 final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo();
2023                 if (info == null) {
2024                     return null;
2025                 }
2026                 info.updateDimensions(context.getResources().getDisplayMetrics());
2027                 return info;
2028             } catch (RemoteException e) {
2029                 throw e.rethrowAsRuntimeException();
2030             }
2031         }
2032 
2033         /**
2034          * Any extras sent by the requesting app.
2035          *
2036          * @return For a shortcut request, this method always return null.  For an AppWidget
2037          * request, this method returns the extras passed to the
2038          * {@link android.appwidget.AppWidgetManager#requestPinAppWidget(
2039          * ComponentName, Bundle, PendingIntent)} API.  See {@link AppWidgetManager} for details.
2040          */
2041         @Nullable
getExtras()2042         public Bundle getExtras() {
2043             try {
2044                 return mInner.getExtras();
2045             } catch (RemoteException e) {
2046                 throw e.rethrowAsRuntimeException();
2047             }
2048         }
2049 
2050         /**
2051          * Return whether a request is still valid.
2052          *
2053          * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called.
2054          */
isValid()2055         public boolean isValid() {
2056             try {
2057                 return mInner.isValid();
2058             } catch (RemoteException e) {
2059                 return false;
2060             }
2061         }
2062 
2063         /**
2064          * Called by the receiving launcher app when the user accepts the request.
2065          *
2066          * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request.
2067          *
2068          * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
2069          * {@code FALSE} if the item hasn't been pinned, for example, because the request had
2070          * already been canceled, in which case the launcher must not pin the requested item.
2071          */
accept(@ullable Bundle options)2072         public boolean accept(@Nullable Bundle options) {
2073             try {
2074                 return mInner.accept(options);
2075             } catch (RemoteException e) {
2076                 throw e.rethrowFromSystemServer();
2077             }
2078         }
2079 
2080         /**
2081          * Called by the receiving launcher app when the user accepts the request, with no options.
2082          *
2083          * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
2084          * {@code FALSE} if the item hasn't been pinned, for example, because the request had
2085          * already been canceled, in which case the launcher must not pin the requested item.
2086          */
accept()2087         public boolean accept() {
2088             return accept(/* options= */ null);
2089         }
2090 
PinItemRequest(Parcel source)2091         private PinItemRequest(Parcel source) {
2092             final ClassLoader cl = getClass().getClassLoader();
2093 
2094             mRequestType = source.readInt();
2095             mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder());
2096         }
2097 
2098         @Override
writeToParcel(Parcel dest, int flags)2099         public void writeToParcel(Parcel dest, int flags) {
2100             dest.writeInt(mRequestType);
2101             dest.writeStrongBinder(mInner.asBinder());
2102         }
2103 
2104         public static final @android.annotation.NonNull Creator<PinItemRequest> CREATOR =
2105                 new Creator<PinItemRequest>() {
2106                     public PinItemRequest createFromParcel(Parcel source) {
2107                         return new PinItemRequest(source);
2108                     }
2109                     public PinItemRequest[] newArray(int size) {
2110                         return new PinItemRequest[size];
2111                     }
2112                 };
2113 
2114         @Override
describeContents()2115         public int describeContents() {
2116             return 0;
2117         }
2118     }
2119 
2120     /**
2121      * A class that encapsulates information about the usage limit set for an app or
2122      * a group of apps.
2123      *
2124      * <p>The launcher can query specifics about the usage limit such as how much usage time
2125      * the limit has and how much of the total usage time is remaining via the APIs available
2126      * in this class.
2127      *
2128      * @see #getAppUsageLimit(String, UserHandle)
2129      * @hide
2130      */
2131     @SystemApi
2132     public static final class AppUsageLimit implements Parcelable {
2133         private final long mTotalUsageLimit;
2134         private final long mUsageRemaining;
2135 
2136         /** @hide */
AppUsageLimit(long totalUsageLimit, long usageRemaining)2137         public AppUsageLimit(long totalUsageLimit, long usageRemaining) {
2138             this.mTotalUsageLimit = totalUsageLimit;
2139             this.mUsageRemaining = usageRemaining;
2140         }
2141 
2142         /**
2143          * Returns the total usage limit in milliseconds set for an app or a group of apps.
2144          *
2145          * @return the total usage limit in milliseconds
2146          */
getTotalUsageLimit()2147         public long getTotalUsageLimit() {
2148             return mTotalUsageLimit;
2149         }
2150 
2151         /**
2152          * Returns the usage remaining in milliseconds for an app or the group of apps
2153          * this limit refers to.
2154          *
2155          * @return the usage remaining in milliseconds
2156          */
getUsageRemaining()2157         public long getUsageRemaining() {
2158             return mUsageRemaining;
2159         }
2160 
AppUsageLimit(Parcel source)2161         private AppUsageLimit(Parcel source) {
2162             mTotalUsageLimit = source.readLong();
2163             mUsageRemaining = source.readLong();
2164         }
2165 
2166         public static final @android.annotation.NonNull Creator<AppUsageLimit> CREATOR = new Creator<AppUsageLimit>() {
2167             @Override
2168             public AppUsageLimit createFromParcel(Parcel source) {
2169                 return new AppUsageLimit(source);
2170             }
2171 
2172             @Override
2173             public AppUsageLimit[] newArray(int size) {
2174                 return new AppUsageLimit[size];
2175             }
2176         };
2177 
2178         @Override
describeContents()2179         public int describeContents() {
2180             return 0;
2181         }
2182 
2183         @Override
writeToParcel(Parcel dest, int flags)2184         public void writeToParcel(Parcel dest, int flags) {
2185             dest.writeLong(mTotalUsageLimit);
2186             dest.writeLong(mUsageRemaining);
2187         }
2188     }
2189 }
2190