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