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