OSDN Git Service

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