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