LauncherApps.java revision f8880561e67e1da246970b49b14285efd4164ab1
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
17package android.content.pm;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ILauncherApps;
23import android.content.pm.IOnAppsChangedListener;
24import android.content.pm.PackageManager.ApplicationInfoFlags;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.graphics.Rect;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.UserHandle;
33import android.os.UserManager;
34import android.util.Log;
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.List;
39
40/**
41 * Class for retrieving a list of launchable activities for the current user and any associated
42 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
43 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
44 * for package changes here.
45 * <p>
46 * To watch for managed profiles being added or removed, register for the following broadcasts:
47 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
48 * <p>
49 * You can retrieve the list of profiles associated with this user with
50 * {@link UserManager#getUserProfiles()}.
51 */
52public class LauncherApps {
53
54    static final String TAG = "LauncherApps";
55    static final boolean DEBUG = false;
56
57    private Context mContext;
58    private ILauncherApps mService;
59    private PackageManager mPm;
60
61    private List<CallbackMessageHandler> mCallbacks
62            = new ArrayList<CallbackMessageHandler>();
63
64    /**
65     * Callbacks for package changes to this and related managed profiles.
66     */
67    public static abstract class Callback {
68        /**
69         * Indicates that a package was removed from the specified profile.
70         *
71         * If a package is removed while being updated onPackageChanged will be
72         * called instead.
73         *
74         * @param packageName The name of the package that was removed.
75         * @param user The UserHandle of the profile that generated the change.
76         */
77        abstract public void onPackageRemoved(String packageName, UserHandle user);
78
79        /**
80         * Indicates that a package was added to the specified profile.
81         *
82         * If a package is added while being updated then onPackageChanged will be
83         * called instead.
84         *
85         * @param packageName The name of the package that was added.
86         * @param user The UserHandle of the profile that generated the change.
87         */
88        abstract public void onPackageAdded(String packageName, UserHandle user);
89
90        /**
91         * Indicates that a package was modified in the specified profile.
92         * This can happen, for example, when the package is updated or when
93         * one or more components are enabled or disabled.
94         *
95         * @param packageName The name of the package that has changed.
96         * @param user The UserHandle of the profile that generated the change.
97         */
98        abstract public void onPackageChanged(String packageName, UserHandle user);
99
100        /**
101         * Indicates that one or more packages have become available. For
102         * example, this can happen when a removable storage card has
103         * reappeared.
104         *
105         * @param packageNames The names of the packages that have become
106         *            available.
107         * @param user The UserHandle of the profile that generated the change.
108         * @param replacing Indicates whether these packages are replacing
109         *            existing ones.
110         */
111        abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
112                boolean replacing);
113
114        /**
115         * Indicates that one or more packages have become unavailable. For
116         * example, this can happen when a removable storage card has been
117         * removed.
118         *
119         * @param packageNames The names of the packages that have become
120         *            unavailable.
121         * @param user The UserHandle of the profile that generated the change.
122         * @param replacing Indicates whether the packages are about to be
123         *            replaced with new versions.
124         */
125        abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
126                boolean replacing);
127
128        /**
129         * Indicates that one or more packages have been suspended. For
130         * example, this can happen when a Device Administrator suspends
131         * an applicaton.
132         *
133         * @param packageNames The names of the packages that have just been
134         *            suspended.
135         * @param user The UserHandle of the profile that generated the change.
136         */
137        public void onPackagesSuspended(String[] packageNames, UserHandle user) {
138        }
139
140        /**
141         * Indicates that one or more packages have been unsuspended. For
142         * example, this can happen when a Device Administrator unsuspends
143         * an applicaton.
144         *
145         * @param packageNames The names of the packages that have just been
146         *            unsuspended.
147         * @param user The UserHandle of the profile that generated the change.
148         */
149        public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
150        }
151    }
152
153    /** @hide */
154    public LauncherApps(Context context, ILauncherApps service) {
155        mContext = context;
156        mService = service;
157        mPm = context.getPackageManager();
158    }
159
160    /**
161     * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
162     * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
163     *
164     * @param packageName The specific package to query. If null, it checks all installed packages
165     *            in the profile.
166     * @param user The UserHandle of the profile.
167     * @return List of launchable activities. Can be an empty list but will not be null.
168     */
169    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
170        ParceledListSlice<ResolveInfo> activities = null;
171        try {
172            activities = mService.getLauncherActivities(packageName, user);
173        } catch (RemoteException re) {
174            throw re.rethrowFromSystemServer();
175        }
176        if (activities == null) {
177            return Collections.EMPTY_LIST;
178        }
179        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
180        for (ResolveInfo ri : activities.getList()) {
181            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
182            if (DEBUG) {
183                Log.v(TAG, "Returning activity for profile " + user + " : "
184                        + lai.getComponentName());
185            }
186            lais.add(lai);
187        }
188        return lais;
189    }
190
191    static ComponentName getComponentName(ResolveInfo ri) {
192        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
193    }
194
195    /**
196     * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
197     * returns null.
198     *
199     * @param intent The intent to find a match for.
200     * @param user The profile to look in for a match.
201     * @return An activity info object if there is a match.
202     */
203    public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
204        try {
205            ResolveInfo ri = mService.resolveActivity(intent, user);
206            if (ri != null) {
207                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
208                return info;
209            }
210        } catch (RemoteException re) {
211            throw re.rethrowFromSystemServer();
212        }
213        return null;
214    }
215
216    /**
217     * Starts a Main activity in the specified profile.
218     *
219     * @param component The ComponentName of the activity to launch
220     * @param user The UserHandle of the profile
221     * @param sourceBounds The Rect containing the source bounds of the clicked icon
222     * @param opts Options to pass to startActivity
223     */
224    public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
225            Bundle opts) {
226        if (DEBUG) {
227            Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
228        }
229        try {
230            mService.startActivityAsUser(component, sourceBounds, opts, user);
231        } catch (RemoteException re) {
232            throw re.rethrowFromSystemServer();
233        }
234    }
235
236    /**
237     * Starts the settings activity to show the application details for a
238     * package in the specified profile.
239     *
240     * @param component The ComponentName of the package to launch settings for.
241     * @param user The UserHandle of the profile
242     * @param sourceBounds The Rect containing the source bounds of the clicked icon
243     * @param opts Options to pass to startActivity
244     */
245    public void startAppDetailsActivity(ComponentName component, UserHandle user,
246            Rect sourceBounds, Bundle opts) {
247        try {
248            mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
249        } catch (RemoteException re) {
250            throw re.rethrowFromSystemServer();
251        }
252    }
253
254    /**
255     * Checks if the package is installed and enabled for a profile.
256     *
257     * @param packageName The package to check.
258     * @param user The UserHandle of the profile.
259     *
260     * @return true if the package exists and is enabled.
261     */
262    public boolean isPackageEnabled(String packageName, UserHandle user) {
263        try {
264            return mService.isPackageEnabled(packageName, user);
265        } catch (RemoteException re) {
266            throw re.rethrowFromSystemServer();
267        }
268    }
269
270    /**
271     * Retrieve all of the information we know about a particular package / application.
272     *
273     * @param packageName The package of the application
274     * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
275     * @param user The UserHandle of the profile.
276     *
277     * @return An {@link ApplicationInfo} containing information about the package or
278     *         null of the package isn't found.
279     */
280    public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
281            UserHandle user) {
282        try {
283            return mService.getApplicationInfo(packageName, flags, user);
284        } catch (RemoteException re) {
285            throw re.rethrowFromSystemServer();
286        }
287    }
288
289    /**
290     * Checks if the activity exists and it enabled for a profile.
291     *
292     * @param component The activity to check.
293     * @param user The UserHandle of the profile.
294     *
295     * @return true if the activity exists and is enabled.
296     */
297    public boolean isActivityEnabled(ComponentName component, UserHandle user) {
298        try {
299            return mService.isActivityEnabled(component, user);
300        } catch (RemoteException re) {
301            throw re.rethrowFromSystemServer();
302        }
303    }
304
305
306    /**
307     * Registers a callback for changes to packages in current and managed profiles.
308     *
309     * @param callback The callback to register.
310     */
311    public void registerCallback(Callback callback) {
312        registerCallback(callback, null);
313    }
314
315    /**
316     * Registers a callback for changes to packages in current and managed profiles.
317     *
318     * @param callback The callback to register.
319     * @param handler that should be used to post callbacks on, may be null.
320     */
321    public void registerCallback(Callback callback, Handler handler) {
322        synchronized (this) {
323            if (callback != null && findCallbackLocked(callback) < 0) {
324                boolean addedFirstCallback = mCallbacks.size() == 0;
325                addCallbackLocked(callback, handler);
326                if (addedFirstCallback) {
327                    try {
328                        mService.addOnAppsChangedListener(mAppsChangedListener);
329                    } catch (RemoteException re) {
330                        throw re.rethrowFromSystemServer();
331                    }
332                }
333            }
334        }
335    }
336
337    /**
338     * Unregisters a callback that was previously registered.
339     *
340     * @param callback The callback to unregister.
341     * @see #registerCallback(Callback)
342     */
343    public void unregisterCallback(Callback callback) {
344        synchronized (this) {
345            removeCallbackLocked(callback);
346            if (mCallbacks.size() == 0) {
347                try {
348                    mService.removeOnAppsChangedListener(mAppsChangedListener);
349                } catch (RemoteException re) {
350                    throw re.rethrowFromSystemServer();
351                }
352            }
353        }
354    }
355
356    /** @return position in mCallbacks for callback or -1 if not present. */
357    private int findCallbackLocked(Callback callback) {
358        if (callback == null) {
359            throw new IllegalArgumentException("Callback cannot be null");
360        }
361        final int size = mCallbacks.size();
362        for (int i = 0; i < size; ++i) {
363            if (mCallbacks.get(i).mCallback == callback) {
364                return i;
365            }
366        }
367        return -1;
368    }
369
370    private void removeCallbackLocked(Callback callback) {
371        int pos = findCallbackLocked(callback);
372        if (pos >= 0) {
373            mCallbacks.remove(pos);
374        }
375    }
376
377    private void addCallbackLocked(Callback callback, Handler handler) {
378        // Remove if already present.
379        removeCallbackLocked(callback);
380        if (handler == null) {
381            handler = new Handler();
382        }
383        CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
384        mCallbacks.add(toAdd);
385    }
386
387    private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
388
389        @Override
390        public void onPackageRemoved(UserHandle user, String packageName)
391                throws RemoteException {
392            if (DEBUG) {
393                Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
394            }
395            synchronized (LauncherApps.this) {
396                for (CallbackMessageHandler callback : mCallbacks) {
397                    callback.postOnPackageRemoved(packageName, user);
398                }
399            }
400        }
401
402        @Override
403        public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
404            if (DEBUG) {
405                Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
406            }
407            synchronized (LauncherApps.this) {
408                for (CallbackMessageHandler callback : mCallbacks) {
409                    callback.postOnPackageChanged(packageName, user);
410                }
411            }
412        }
413
414        @Override
415        public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
416            if (DEBUG) {
417                Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
418            }
419            synchronized (LauncherApps.this) {
420                for (CallbackMessageHandler callback : mCallbacks) {
421                    callback.postOnPackageAdded(packageName, user);
422                }
423            }
424        }
425
426        @Override
427        public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
428                throws RemoteException {
429            if (DEBUG) {
430                Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
431            }
432            synchronized (LauncherApps.this) {
433                for (CallbackMessageHandler callback : mCallbacks) {
434                    callback.postOnPackagesAvailable(packageNames, user, replacing);
435                }
436            }
437        }
438
439        @Override
440        public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
441                throws RemoteException {
442            if (DEBUG) {
443                Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
444            }
445            synchronized (LauncherApps.this) {
446                for (CallbackMessageHandler callback : mCallbacks) {
447                    callback.postOnPackagesUnavailable(packageNames, user, replacing);
448                }
449            }
450        }
451
452        @Override
453        public void onPackagesSuspended(UserHandle user, String[] packageNames)
454                throws RemoteException {
455            if (DEBUG) {
456                Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
457            }
458            synchronized (LauncherApps.this) {
459                for (CallbackMessageHandler callback : mCallbacks) {
460                    callback.postOnPackagesSuspended(packageNames, user);
461                }
462            }
463        }
464
465        @Override
466        public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
467                throws RemoteException {
468            if (DEBUG) {
469                Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
470            }
471            synchronized (LauncherApps.this) {
472                for (CallbackMessageHandler callback : mCallbacks) {
473                    callback.postOnPackagesUnsuspended(packageNames, user);
474                }
475            }
476        }
477    };
478
479    private static class CallbackMessageHandler extends Handler {
480        private static final int MSG_ADDED = 1;
481        private static final int MSG_REMOVED = 2;
482        private static final int MSG_CHANGED = 3;
483        private static final int MSG_AVAILABLE = 4;
484        private static final int MSG_UNAVAILABLE = 5;
485        private static final int MSG_SUSPENDED = 6;
486        private static final int MSG_UNSUSPENDED = 7;
487
488        private LauncherApps.Callback mCallback;
489
490        private static class CallbackInfo {
491            String[] packageNames;
492            String packageName;
493            boolean replacing;
494            UserHandle user;
495        }
496
497        public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
498            super(looper, null, true);
499            mCallback = callback;
500        }
501
502        @Override
503        public void handleMessage(Message msg) {
504            if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
505                return;
506            }
507            CallbackInfo info = (CallbackInfo) msg.obj;
508            switch (msg.what) {
509                case MSG_ADDED:
510                    mCallback.onPackageAdded(info.packageName, info.user);
511                    break;
512                case MSG_REMOVED:
513                    mCallback.onPackageRemoved(info.packageName, info.user);
514                    break;
515                case MSG_CHANGED:
516                    mCallback.onPackageChanged(info.packageName, info.user);
517                    break;
518                case MSG_AVAILABLE:
519                    mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
520                    break;
521                case MSG_UNAVAILABLE:
522                    mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
523                    break;
524                case MSG_SUSPENDED:
525                    mCallback.onPackagesSuspended(info.packageNames, info.user);
526                    break;
527                case MSG_UNSUSPENDED:
528                    mCallback.onPackagesUnsuspended(info.packageNames, info.user);
529                    break;
530            }
531        }
532
533        public void postOnPackageAdded(String packageName, UserHandle user) {
534            CallbackInfo info = new CallbackInfo();
535            info.packageName = packageName;
536            info.user = user;
537            obtainMessage(MSG_ADDED, info).sendToTarget();
538        }
539
540        public void postOnPackageRemoved(String packageName, UserHandle user) {
541            CallbackInfo info = new CallbackInfo();
542            info.packageName = packageName;
543            info.user = user;
544            obtainMessage(MSG_REMOVED, info).sendToTarget();
545        }
546
547        public void postOnPackageChanged(String packageName, UserHandle user) {
548            CallbackInfo info = new CallbackInfo();
549            info.packageName = packageName;
550            info.user = user;
551            obtainMessage(MSG_CHANGED, info).sendToTarget();
552        }
553
554        public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
555                boolean replacing) {
556            CallbackInfo info = new CallbackInfo();
557            info.packageNames = packageNames;
558            info.replacing = replacing;
559            info.user = user;
560            obtainMessage(MSG_AVAILABLE, info).sendToTarget();
561        }
562
563        public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
564                boolean replacing) {
565            CallbackInfo info = new CallbackInfo();
566            info.packageNames = packageNames;
567            info.replacing = replacing;
568            info.user = user;
569            obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
570        }
571
572        public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
573            CallbackInfo info = new CallbackInfo();
574            info.packageNames = packageNames;
575            info.user = user;
576            obtainMessage(MSG_SUSPENDED, info).sendToTarget();
577        }
578
579        public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) {
580            CallbackInfo info = new CallbackInfo();
581            info.packageNames = packageNames;
582            info.user = user;
583            obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
584        }
585    }
586}
587