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