OSDN Git Service

2133222
[android-x86/frameworks-base.git] /
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.content.pm;
18
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.TestApi;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager.ApplicationInfoFlags;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.res.Resources;
29 import android.graphics.Bitmap;
30 import android.graphics.BitmapFactory;
31 import android.graphics.Rect;
32 import android.graphics.drawable.BitmapDrawable;
33 import android.graphics.drawable.Drawable;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.Looper;
37 import android.os.Message;
38 import android.os.ParcelFileDescriptor;
39 import android.os.RemoteException;
40 import android.os.ServiceManager;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.util.DisplayMetrics;
44 import android.util.Log;
45
46 import java.io.IOException;
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.List;
53
54 /**
55  * Class for retrieving a list of launchable activities for the current user and any associated
56  * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
57  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
58  * for package changes here.
59  * <p>
60  * To watch for managed profiles being added or removed, register for the following broadcasts:
61  * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
62  * <p>
63  * You can retrieve the list of profiles associated with this user with
64  * {@link UserManager#getUserProfiles()}.
65  */
66 public class LauncherApps {
67
68     static final String TAG = "LauncherApps";
69     static final boolean DEBUG = false;
70
71     private Context mContext;
72     private ILauncherApps mService;
73     private PackageManager mPm;
74
75     private List<CallbackMessageHandler> mCallbacks
76             = new ArrayList<CallbackMessageHandler>();
77
78     /**
79      * Callbacks for package changes to this and related managed profiles.
80      */
81     public static abstract class Callback {
82         /**
83          * Indicates that a package was removed from the specified profile.
84          *
85          * If a package is removed while being updated onPackageChanged will be
86          * called instead.
87          *
88          * @param packageName The name of the package that was removed.
89          * @param user The UserHandle of the profile that generated the change.
90          */
91         abstract public void onPackageRemoved(String packageName, UserHandle user);
92
93         /**
94          * Indicates that a package was added to the specified profile.
95          *
96          * If a package is added while being updated then onPackageChanged will be
97          * called instead.
98          *
99          * @param packageName The name of the package that was added.
100          * @param user The UserHandle of the profile that generated the change.
101          */
102         abstract public void onPackageAdded(String packageName, UserHandle user);
103
104         /**
105          * Indicates that a package was modified in the specified profile.
106          * This can happen, for example, when the package is updated or when
107          * one or more components are enabled or disabled.
108          *
109          * @param packageName The name of the package that has changed.
110          * @param user The UserHandle of the profile that generated the change.
111          */
112         abstract public void onPackageChanged(String packageName, UserHandle user);
113
114         /**
115          * Indicates that one or more packages have become available. For
116          * example, this can happen when a removable storage card has
117          * reappeared.
118          *
119          * @param packageNames The names of the packages that have become
120          *            available.
121          * @param user The UserHandle of the profile that generated the change.
122          * @param replacing Indicates whether these packages are replacing
123          *            existing ones.
124          */
125         abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
126                 boolean replacing);
127
128         /**
129          * Indicates that one or more packages have become unavailable. For
130          * example, this can happen when a removable storage card has been
131          * removed.
132          *
133          * @param packageNames The names of the packages that have become
134          *            unavailable.
135          * @param user The UserHandle of the profile that generated the change.
136          * @param replacing Indicates whether the packages are about to be
137          *            replaced with new versions.
138          */
139         abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
140                 boolean replacing);
141
142         /**
143          * Indicates that one or more packages have been suspended. For
144          * example, this can happen when a Device Administrator suspends
145          * an applicaton.
146          *
147          * @param packageNames The names of the packages that have just been
148          *            suspended.
149          * @param user The UserHandle of the profile that generated the change.
150          */
151         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
152         }
153
154         /**
155          * Indicates that one or more packages have been unsuspended. For
156          * example, this can happen when a Device Administrator unsuspends
157          * an applicaton.
158          *
159          * @param packageNames The names of the packages that have just been
160          *            unsuspended.
161          * @param user The UserHandle of the profile that generated the change.
162          */
163         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
164         }
165
166         /**
167          * Indicates that one or more shortcuts of any kinds (dynamic, pinned, or manifest)
168          * have been added, updated or removed.
169          *
170          * <p>Only the applications that are allowed to access the shortcut information,
171          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
172          *
173          * @param packageName The name of the package that has the shortcuts.
174          * @param shortcuts all shortcuts from the package (dynamic, manifest and/or pinned) will
175          *    be passed. Only "key" information will be provided, as defined in
176          *    {@link ShortcutInfo#hasKeyFieldsOnly()}.
177          * @param user The UserHandle of the profile that generated the change.
178          *
179          * @see ShortcutManager
180          */
181         public void onShortcutsChanged(@NonNull String packageName,
182                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
183         }
184     }
185
186     /**
187      * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
188      */
189     public static class ShortcutQuery {
190         /**
191          * Include dynamic shortcuts in the result.
192          */
193         public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
194
195         /** @hide kept for unit tests */
196         @Deprecated
197         public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC;
198
199         /**
200          * Include pinned shortcuts in the result.
201          */
202         public static final int FLAG_MATCH_PINNED = 1 << 1;
203
204         /** @hide kept for unit tests */
205         @Deprecated
206         public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED;
207
208         /**
209          * Include manifest shortcuts in the result.
210          */
211         public static final int FLAG_MATCH_MANIFEST = 1 << 3;
212
213         /** @hide kept for unit tests */
214         @Deprecated
215         public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
216
217         /** @hide */
218         public static final int FLAG_MATCH_ALL_KINDS =
219                 FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
220
221         /** @hide kept for unit tests */
222         @Deprecated
223         public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS;
224
225         /**
226          * Requests "key" fields only.  See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
227          * fields are available.  This allows quicker access to shortcut information in order to
228          * determine in-memory cache in the caller needs to be updated.
229          *
230          * <p>Typically, launcher applications cache all or most shortcuts' information
231          * in memory in order to show shortcuts without a delay.  When they want to update their
232          * cache (e.g. when their process restart), they can fetch all shortcuts' information with
233          * with this flag, then check {@link ShortcutInfo#getLastChangedTimestamp()} for each
234          * shortcut and issue a second call to fetch the non-key information of only updated
235          * shortcuts.
236          *
237          * @see ShortcutManager
238          */
239         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
240
241         /** @hide */
242         @IntDef(flag = true,
243                 value = {
244                         FLAG_MATCH_DYNAMIC,
245                         FLAG_MATCH_PINNED,
246                         FLAG_MATCH_MANIFEST,
247                         FLAG_GET_KEY_FIELDS_ONLY,
248                 })
249         @Retention(RetentionPolicy.SOURCE)
250         public @interface QueryFlags {}
251
252         long mChangedSince;
253
254         @Nullable
255         String mPackage;
256
257         @Nullable
258         List<String> mShortcutIds;
259
260         @Nullable
261         ComponentName mActivity;
262
263         @QueryFlags
264         int mQueryFlags;
265
266         public ShortcutQuery() {
267         }
268
269         /**
270          * If non-zero, returns only shortcuts that have been added or updated since the timestamp.
271          * Units are as per {@link System#currentTimeMillis()}.
272          */
273         public ShortcutQuery setChangedSince(long changedSince) {
274             mChangedSince = changedSince;
275             return this;
276         }
277
278         /**
279          * If non-null, returns only shortcuts from the package.
280          */
281         public ShortcutQuery setPackage(@Nullable String packageName) {
282             mPackage = packageName;
283             return this;
284         }
285
286         /**
287          * If non-null, return only the specified shortcuts by ID.  When setting this field,
288          * a package name must also be set with {@link #setPackage}.
289          */
290         public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
291             mShortcutIds = shortcutIds;
292             return this;
293         }
294
295         /**
296          * If non-null, returns only shortcuts associated with the activity; i.e.
297          * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
298          * to {@code activity}.
299          */
300         public ShortcutQuery setActivity(@Nullable ComponentName activity) {
301             mActivity = activity;
302             return this;
303         }
304
305         /**
306          * Set query options.  At least one of the {@code MATCH} flags should be set.  (Otherwise
307          * no shortcuts will be returned.)
308          *
309          * @see {@link #FLAG_MATCH_DYNAMIC}
310          * @see {@link #FLAG_MATCH_PINNED}
311          * @see {@link #FLAG_MATCH_MANIFEST}
312          * @see {@link #FLAG_GET_KEY_FIELDS_ONLY}
313          */
314         public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
315             mQueryFlags = queryFlags;
316             return this;
317         }
318     }
319
320     /** @hide */
321     public LauncherApps(Context context, ILauncherApps service) {
322         mContext = context;
323         mService = service;
324         mPm = context.getPackageManager();
325     }
326
327     /** @hide */
328     @TestApi
329     public LauncherApps(Context context) {
330         this(context, ILauncherApps.Stub.asInterface(
331                 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
332     }
333
334     /**
335      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
336      * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
337      *
338      * @param packageName The specific package to query. If null, it checks all installed packages
339      *            in the profile.
340      * @param user The UserHandle of the profile.
341      * @return List of launchable activities. Can be an empty list but will not be null.
342      */
343     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
344         ParceledListSlice<ResolveInfo> activities = null;
345         try {
346             activities = mService.getLauncherActivities(packageName, user);
347         } catch (RemoteException re) {
348             throw re.rethrowFromSystemServer();
349         }
350         if (activities == null) {
351             return Collections.EMPTY_LIST;
352         }
353         ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
354         for (ResolveInfo ri : activities.getList()) {
355             LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
356             if (DEBUG) {
357                 Log.v(TAG, "Returning activity for profile " + user + " : "
358                         + lai.getComponentName());
359             }
360             lais.add(lai);
361         }
362         return lais;
363     }
364
365     /**
366      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
367      * returns null.
368      *
369      * @param intent The intent to find a match for.
370      * @param user The profile to look in for a match.
371      * @return An activity info object if there is a match.
372      */
373     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
374         try {
375             ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
376             if (ai != null) {
377                 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
378                 return info;
379             }
380         } catch (RemoteException re) {
381             throw re.rethrowFromSystemServer();
382         }
383         return null;
384     }
385
386     /**
387      * Starts a Main activity in the specified profile.
388      *
389      * @param component The ComponentName of the activity to launch
390      * @param user The UserHandle of the profile
391      * @param sourceBounds The Rect containing the source bounds of the clicked icon
392      * @param opts Options to pass to startActivity
393      */
394     public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
395             Bundle opts) {
396         if (DEBUG) {
397             Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
398         }
399         try {
400             mService.startActivityAsUser(component, sourceBounds, opts, user);
401         } catch (RemoteException re) {
402             throw re.rethrowFromSystemServer();
403         }
404     }
405
406     /**
407      * Starts the settings activity to show the application details for a
408      * package in the specified profile.
409      *
410      * @param component The ComponentName of the package to launch settings for.
411      * @param user The UserHandle of the profile
412      * @param sourceBounds The Rect containing the source bounds of the clicked icon
413      * @param opts Options to pass to startActivity
414      */
415     public void startAppDetailsActivity(ComponentName component, UserHandle user,
416             Rect sourceBounds, Bundle opts) {
417         try {
418             mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
419         } catch (RemoteException re) {
420             throw re.rethrowFromSystemServer();
421         }
422     }
423
424     /**
425      * Checks if the package is installed and enabled for a profile.
426      *
427      * @param packageName The package to check.
428      * @param user The UserHandle of the profile.
429      *
430      * @return true if the package exists and is enabled.
431      */
432     public boolean isPackageEnabled(String packageName, UserHandle user) {
433         try {
434             return mService.isPackageEnabled(packageName, user);
435         } catch (RemoteException re) {
436             throw re.rethrowFromSystemServer();
437         }
438     }
439
440     /**
441      * Retrieve all of the information we know about a particular package / application.
442      *
443      * @param packageName The package of the application
444      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
445      * @param user The UserHandle of the profile.
446      *
447      * @return An {@link ApplicationInfo} containing information about the package or
448      *         null of the package isn't found.
449      * @hide
450      */
451     public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
452             UserHandle user) {
453         try {
454             return mService.getApplicationInfo(packageName, flags, user);
455         } catch (RemoteException re) {
456             throw re.rethrowFromSystemServer();
457         }
458     }
459
460     /**
461      * Checks if the activity exists and it enabled for a profile.
462      *
463      * @param component The activity to check.
464      * @param user The UserHandle of the profile.
465      *
466      * @return true if the activity exists and is enabled.
467      */
468     public boolean isActivityEnabled(ComponentName component, UserHandle user) {
469         try {
470             return mService.isActivityEnabled(component, user);
471         } catch (RemoteException re) {
472             throw re.rethrowFromSystemServer();
473         }
474     }
475
476     /**
477      * Returns whether the caller can access the shortcut information.
478      *
479      * <p>Only the default launcher can access the shortcut information.
480      *
481      * <p>Note when this method returns {@code false}, it may be a temporary situation because
482      * the user is trying a new launcher application.  The user may decide to change the default
483      * launcher back to the calling application again, so even if a launcher application loses
484      * this permission, it does <b>not</b> have to purge pinned shortcut information.
485      * Also in this situation, pinned shortcuts can still be started, even though the caller
486      * no longer has the shortcut host permission.
487      *
488      * @see ShortcutManager
489      */
490     public boolean hasShortcutHostPermission() {
491         try {
492             return mService.hasShortcutHostPermission(mContext.getPackageName());
493         } catch (RemoteException re) {
494             throw re.rethrowFromSystemServer();
495         }
496     }
497
498     /**
499      * Returns {@link ShortcutInfo}s that match {@code query}.
500      *
501      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
502      * #hasShortcutHostPermission()}.
503      *
504      * @param query result includes shortcuts matching this query.
505      * @param user The UserHandle of the profile.
506      *
507      * @return the IDs of {@link ShortcutInfo}s that match the query.
508      *
509      * @see ShortcutManager
510      */
511     @Nullable
512     public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
513             @NonNull UserHandle user) {
514         try {
515             return mService.getShortcuts(mContext.getPackageName(),
516                     query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
517                     query.mQueryFlags, user)
518                     .getList();
519         } catch (RemoteException e) {
520             throw e.rethrowFromSystemServer();
521         }
522     }
523
524     /**
525      * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
526      */
527     @Nullable
528     @Deprecated
529     public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
530             @NonNull List<String> ids, @NonNull UserHandle user) {
531         final ShortcutQuery q = new ShortcutQuery();
532         q.setPackage(packageName);
533         q.setShortcutIds(ids);
534         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
535         return getShortcuts(q, user);
536     }
537
538     /**
539      * Pin shortcuts on a package.
540      *
541      * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
542      * However, different launchers may have different set of pinned shortcuts.
543      *
544      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
545      * #hasShortcutHostPermission()}.
546      *
547      * @param packageName The target package name.
548      * @param shortcutIds The IDs of the shortcut to be pinned.
549      * @param user The UserHandle of the profile.
550      *
551      * @see ShortcutManager
552      */
553     public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
554             @NonNull UserHandle user) {
555         try {
556             mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
557         } catch (RemoteException e) {
558             throw e.rethrowFromSystemServer();
559         }
560     }
561
562     /**
563      * @hide kept for testing.
564      */
565     @Deprecated
566     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
567         return shortcut.getIconResourceId();
568     }
569
570     /**
571      * @hide kept for testing.
572      */
573     @Deprecated
574     public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
575             @NonNull UserHandle user) {
576         final ShortcutQuery q = new ShortcutQuery();
577         q.setPackage(packageName);
578         q.setShortcutIds(Arrays.asList(shortcutId));
579         q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
580         final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
581
582         return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
583     }
584
585     /**
586      * @hide internal/unit tests only
587      */
588     public ParcelFileDescriptor getShortcutIconFd(
589             @NonNull ShortcutInfo shortcut) {
590         return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
591                 shortcut.getUserId());
592     }
593
594     /**
595      * @hide internal/unit tests only
596      */
597     public ParcelFileDescriptor getShortcutIconFd(
598             @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
599         return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
600     }
601
602     private ParcelFileDescriptor getShortcutIconFd(
603             @NonNull String packageName, @NonNull String shortcutId, int userId) {
604         try {
605             return mService.getShortcutIconFd(mContext.getPackageName(),
606                     packageName, shortcutId, userId);
607         } catch (RemoteException e) {
608             throw e.rethrowFromSystemServer();
609         }
610     }
611
612     /**
613      * Returns the icon for this shortcut, without any badging for the profile.
614      *
615      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
616      * #hasShortcutHostPermission()}.
617      *
618      * @param density The preferred density of the icon, zero for default density. Use
619      * density DPI values from {@link DisplayMetrics}.
620      *
621      * @return The drawable associated with the shortcut.
622      *
623      * @see ShortcutManager
624      * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
625      * @see DisplayMetrics
626      */
627     public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
628         if (shortcut.hasIconFile()) {
629             final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
630             if (pfd == null) {
631                 return null;
632             }
633             try {
634                 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
635                 return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp);
636             } finally {
637                 try {
638                     pfd.close();
639                 } catch (IOException ignore) {
640                 }
641             }
642         } else if (shortcut.hasIconResource()) {
643             try {
644                 final int resId = shortcut.getIconResourceId();
645                 if (resId == 0) {
646                     return null; // Shouldn't happen but just in case.
647                 }
648                 final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
649                         /* flags =*/ 0, shortcut.getUserHandle());
650                 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
651                 return res.getDrawableForDensity(resId, density);
652             } catch (NameNotFoundException | Resources.NotFoundException e) {
653                 return null;
654             }
655         } else {
656             return null; // Has no icon.
657         }
658     }
659
660     /**
661      * Returns the shortcut icon with badging appropriate for the profile.
662      *
663      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
664      * #hasShortcutHostPermission()}.
665      *
666      * @param density Optional density for the icon, or 0 to use the default density. Use
667      * @return A badged icon for the shortcut.
668      *
669      * @see ShortcutManager
670      * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
671      * @see DisplayMetrics
672      */
673     public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
674         final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
675
676         return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
677                 originalIcon, shortcut.getUserHandle());
678     }
679
680     /**
681      * Starts a shortcut.
682      *
683      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
684      * #hasShortcutHostPermission()}.
685      *
686      * @param packageName The target shortcut package name.
687      * @param shortcutId The target shortcut ID.
688      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
689      * @param startActivityOptions Options to pass to startActivity.
690      * @param user The UserHandle of the profile.
691      */
692     public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
693             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
694             @NonNull UserHandle user) {
695         startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
696                 user.getIdentifier());
697     }
698
699     /**
700      * Launches a shortcut.
701      *
702      * <p>Callers must be allowed to access the shortcut information, as defined in {@link
703      * #hasShortcutHostPermission()}.
704      *
705      * @param shortcut The target shortcut.
706      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
707      * @param startActivityOptions Options to pass to startActivity.
708      */
709     public void startShortcut(@NonNull ShortcutInfo shortcut,
710             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
711         startShortcut(shortcut.getPackage(), shortcut.getId(),
712                 sourceBounds, startActivityOptions,
713                 shortcut.getUserId());
714     }
715
716     private void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
717             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
718             int userId) {
719         try {
720             mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
721                     sourceBounds, startActivityOptions, userId);
722         } catch (RemoteException e) {
723             throw e.rethrowFromSystemServer();
724         }
725     }
726
727     /**
728      * Registers a callback for changes to packages in current and managed profiles.
729      *
730      * @param callback The callback to register.
731      */
732     public void registerCallback(Callback callback) {
733         registerCallback(callback, null);
734     }
735
736     /**
737      * Registers a callback for changes to packages in current and managed profiles.
738      *
739      * @param callback The callback to register.
740      * @param handler that should be used to post callbacks on, may be null.
741      */
742     public void registerCallback(Callback callback, Handler handler) {
743         synchronized (this) {
744             if (callback != null && findCallbackLocked(callback) < 0) {
745                 boolean addedFirstCallback = mCallbacks.size() == 0;
746                 addCallbackLocked(callback, handler);
747                 if (addedFirstCallback) {
748                     try {
749                         mService.addOnAppsChangedListener(mContext.getPackageName(),
750                                 mAppsChangedListener);
751                     } catch (RemoteException re) {
752                         throw re.rethrowFromSystemServer();
753                     }
754                 }
755             }
756         }
757     }
758
759     /**
760      * Unregisters a callback that was previously registered.
761      *
762      * @param callback The callback to unregister.
763      * @see #registerCallback(Callback)
764      */
765     public void unregisterCallback(Callback callback) {
766         synchronized (this) {
767             removeCallbackLocked(callback);
768             if (mCallbacks.size() == 0) {
769                 try {
770                     mService.removeOnAppsChangedListener(mAppsChangedListener);
771                 } catch (RemoteException re) {
772                     throw re.rethrowFromSystemServer();
773                 }
774             }
775         }
776     }
777
778     /** @return position in mCallbacks for callback or -1 if not present. */
779     private int findCallbackLocked(Callback callback) {
780         if (callback == null) {
781             throw new IllegalArgumentException("Callback cannot be null");
782         }
783         final int size = mCallbacks.size();
784         for (int i = 0; i < size; ++i) {
785             if (mCallbacks.get(i).mCallback == callback) {
786                 return i;
787             }
788         }
789         return -1;
790     }
791
792     private void removeCallbackLocked(Callback callback) {
793         int pos = findCallbackLocked(callback);
794         if (pos >= 0) {
795             mCallbacks.remove(pos);
796         }
797     }
798
799     private void addCallbackLocked(Callback callback, Handler handler) {
800         // Remove if already present.
801         removeCallbackLocked(callback);
802         if (handler == null) {
803             handler = new Handler();
804         }
805         CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
806         mCallbacks.add(toAdd);
807     }
808
809     private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
810
811         @Override
812         public void onPackageRemoved(UserHandle user, String packageName)
813                 throws RemoteException {
814             if (DEBUG) {
815                 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
816             }
817             synchronized (LauncherApps.this) {
818                 for (CallbackMessageHandler callback : mCallbacks) {
819                     callback.postOnPackageRemoved(packageName, user);
820                 }
821             }
822         }
823
824         @Override
825         public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
826             if (DEBUG) {
827                 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
828             }
829             synchronized (LauncherApps.this) {
830                 for (CallbackMessageHandler callback : mCallbacks) {
831                     callback.postOnPackageChanged(packageName, user);
832                 }
833             }
834         }
835
836         @Override
837         public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
838             if (DEBUG) {
839                 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
840             }
841             synchronized (LauncherApps.this) {
842                 for (CallbackMessageHandler callback : mCallbacks) {
843                     callback.postOnPackageAdded(packageName, user);
844                 }
845             }
846         }
847
848         @Override
849         public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
850                 throws RemoteException {
851             if (DEBUG) {
852                 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
853             }
854             synchronized (LauncherApps.this) {
855                 for (CallbackMessageHandler callback : mCallbacks) {
856                     callback.postOnPackagesAvailable(packageNames, user, replacing);
857                 }
858             }
859         }
860
861         @Override
862         public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
863                 throws RemoteException {
864             if (DEBUG) {
865                 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
866             }
867             synchronized (LauncherApps.this) {
868                 for (CallbackMessageHandler callback : mCallbacks) {
869                     callback.postOnPackagesUnavailable(packageNames, user, replacing);
870                 }
871             }
872         }
873
874         @Override
875         public void onPackagesSuspended(UserHandle user, String[] packageNames)
876                 throws RemoteException {
877             if (DEBUG) {
878                 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
879             }
880             synchronized (LauncherApps.this) {
881                 for (CallbackMessageHandler callback : mCallbacks) {
882                     callback.postOnPackagesSuspended(packageNames, user);
883                 }
884             }
885         }
886
887         @Override
888         public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
889                 throws RemoteException {
890             if (DEBUG) {
891                 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
892             }
893             synchronized (LauncherApps.this) {
894                 for (CallbackMessageHandler callback : mCallbacks) {
895                     callback.postOnPackagesUnsuspended(packageNames, user);
896                 }
897             }
898         }
899
900         @Override
901         public void onShortcutChanged(UserHandle user, String packageName,
902                 ParceledListSlice shortcuts) {
903             if (DEBUG) {
904                 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
905             }
906             final List<ShortcutInfo> list = shortcuts.getList();
907             synchronized (LauncherApps.this) {
908                 for (CallbackMessageHandler callback : mCallbacks) {
909                     callback.postOnShortcutChanged(packageName, user, list);
910                 }
911             }
912         }
913     };
914
915     private static class CallbackMessageHandler extends Handler {
916         private static final int MSG_ADDED = 1;
917         private static final int MSG_REMOVED = 2;
918         private static final int MSG_CHANGED = 3;
919         private static final int MSG_AVAILABLE = 4;
920         private static final int MSG_UNAVAILABLE = 5;
921         private static final int MSG_SUSPENDED = 6;
922         private static final int MSG_UNSUSPENDED = 7;
923         private static final int MSG_SHORTCUT_CHANGED = 8;
924
925         private LauncherApps.Callback mCallback;
926
927         private static class CallbackInfo {
928             String[] packageNames;
929             String packageName;
930             boolean replacing;
931             UserHandle user;
932             List<ShortcutInfo> shortcuts;
933         }
934
935         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
936             super(looper, null, true);
937             mCallback = callback;
938         }
939
940         @Override
941         public void handleMessage(Message msg) {
942             if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
943                 return;
944             }
945             CallbackInfo info = (CallbackInfo) msg.obj;
946             switch (msg.what) {
947                 case MSG_ADDED:
948                     mCallback.onPackageAdded(info.packageName, info.user);
949                     break;
950                 case MSG_REMOVED:
951                     mCallback.onPackageRemoved(info.packageName, info.user);
952                     break;
953                 case MSG_CHANGED:
954                     mCallback.onPackageChanged(info.packageName, info.user);
955                     break;
956                 case MSG_AVAILABLE:
957                     mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
958                     break;
959                 case MSG_UNAVAILABLE:
960                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
961                     break;
962                 case MSG_SUSPENDED:
963                     mCallback.onPackagesSuspended(info.packageNames, info.user);
964                     break;
965                 case MSG_UNSUSPENDED:
966                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
967                     break;
968                 case MSG_SHORTCUT_CHANGED:
969                     mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
970                     break;
971             }
972         }
973
974         public void postOnPackageAdded(String packageName, UserHandle user) {
975             CallbackInfo info = new CallbackInfo();
976             info.packageName = packageName;
977             info.user = user;
978             obtainMessage(MSG_ADDED, info).sendToTarget();
979         }
980
981         public void postOnPackageRemoved(String packageName, UserHandle user) {
982             CallbackInfo info = new CallbackInfo();
983             info.packageName = packageName;
984             info.user = user;
985             obtainMessage(MSG_REMOVED, info).sendToTarget();
986         }
987
988         public void postOnPackageChanged(String packageName, UserHandle user) {
989             CallbackInfo info = new CallbackInfo();
990             info.packageName = packageName;
991             info.user = user;
992             obtainMessage(MSG_CHANGED, info).sendToTarget();
993         }
994
995         public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
996                 boolean replacing) {
997             CallbackInfo info = new CallbackInfo();
998             info.packageNames = packageNames;
999             info.replacing = replacing;
1000             info.user = user;
1001             obtainMessage(MSG_AVAILABLE, info).sendToTarget();
1002         }
1003
1004         public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
1005                 boolean replacing) {
1006             CallbackInfo info = new CallbackInfo();
1007             info.packageNames = packageNames;
1008             info.replacing = replacing;
1009             info.user = user;
1010             obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
1011         }
1012
1013         public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
1014             CallbackInfo info = new CallbackInfo();
1015             info.packageNames = packageNames;
1016             info.user = user;
1017             obtainMessage(MSG_SUSPENDED, info).sendToTarget();
1018         }
1019
1020         public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
1021             CallbackInfo info = new CallbackInfo();
1022             info.packageNames = packageNames;
1023             info.user = user;
1024             obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
1025         }
1026
1027         public void postOnShortcutChanged(String packageName, UserHandle user,
1028                 List<ShortcutInfo> shortcuts) {
1029             CallbackInfo info = new CallbackInfo();
1030             info.packageName = packageName;
1031             info.user = user;
1032             info.shortcuts = shortcuts;
1033             obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
1034         }
1035     }
1036 }