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.app.AppGlobals;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.ILauncherApps;
     24 import android.content.pm.IOnAppsChangedListener;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.graphics.Rect;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.Looper;
     30 import android.os.Message;
     31 import android.os.RemoteException;
     32 import android.os.UserHandle;
     33 import android.os.UserManager;
     34 import android.util.Log;
     35 
     36 import java.util.ArrayList;
     37 import java.util.Collections;
     38 import 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  */
     52 public 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     /** @hide */
    130     public LauncherApps(Context context, ILauncherApps service) {
    131         mContext = context;
    132         mService = service;
    133         mPm = context.getPackageManager();
    134     }
    135 
    136     /**
    137      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
    138      * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
    139      *
    140      * @param packageName The specific package to query. If null, it checks all installed packages
    141      *            in the profile.
    142      * @param user The UserHandle of the profile.
    143      * @return List of launchable activities. Can be an empty list but will not be null.
    144      */
    145     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
    146         List<ResolveInfo> activities = null;
    147         try {
    148             activities = mService.getLauncherActivities(packageName, user);
    149         } catch (RemoteException re) {
    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 && !mCallbacks.contains(callback)) {
    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     private void removeCallbackLocked(Callback callback) {
    329         if (callback == null) {
    330             throw new IllegalArgumentException("Callback cannot be null");
    331         }
    332         final int size = mCallbacks.size();
    333         for (int i = 0; i < size; ++i) {
    334             if (mCallbacks.get(i).mCallback == callback) {
    335                 mCallbacks.remove(i);
    336                 return;
    337             }
    338         }
    339     }
    340 
    341     private void addCallbackLocked(Callback callback, Handler handler) {
    342         // Remove if already present.
    343         removeCallbackLocked(callback);
    344         if (handler == null) {
    345             handler = new Handler();
    346         }
    347         CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
    348         mCallbacks.add(toAdd);
    349     }
    350 
    351     private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
    352 
    353         @Override
    354         public void onPackageRemoved(UserHandle user, String packageName)
    355                 throws RemoteException {
    356             if (DEBUG) {
    357                 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
    358             }
    359             synchronized (LauncherApps.this) {
    360                 for (CallbackMessageHandler callback : mCallbacks) {
    361                     callback.postOnPackageRemoved(packageName, user);
    362                 }
    363             }
    364         }
    365 
    366         @Override
    367         public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
    368             if (DEBUG) {
    369                 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
    370             }
    371             synchronized (LauncherApps.this) {
    372                 for (CallbackMessageHandler callback : mCallbacks) {
    373                     callback.postOnPackageChanged(packageName, user);
    374                 }
    375             }
    376         }
    377 
    378         @Override
    379         public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
    380             if (DEBUG) {
    381                 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
    382             }
    383             synchronized (LauncherApps.this) {
    384                 for (CallbackMessageHandler callback : mCallbacks) {
    385                     callback.postOnPackageAdded(packageName, user);
    386                 }
    387             }
    388         }
    389 
    390         @Override
    391         public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
    392                 throws RemoteException {
    393             if (DEBUG) {
    394                 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
    395             }
    396             synchronized (LauncherApps.this) {
    397                 for (CallbackMessageHandler callback : mCallbacks) {
    398                     callback.postOnPackagesAvailable(packageNames, user, replacing);
    399                 }
    400             }
    401         }
    402 
    403         @Override
    404         public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
    405                 throws RemoteException {
    406             if (DEBUG) {
    407                 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
    408             }
    409             synchronized (LauncherApps.this) {
    410                 for (CallbackMessageHandler callback : mCallbacks) {
    411                     callback.postOnPackagesUnavailable(packageNames, user, replacing);
    412                 }
    413            }
    414         }
    415     };
    416 
    417     private static class CallbackMessageHandler extends Handler {
    418         private static final int MSG_ADDED = 1;
    419         private static final int MSG_REMOVED = 2;
    420         private static final int MSG_CHANGED = 3;
    421         private static final int MSG_AVAILABLE = 4;
    422         private static final int MSG_UNAVAILABLE = 5;
    423 
    424         private LauncherApps.Callback mCallback;
    425 
    426         private static class CallbackInfo {
    427             String[] packageNames;
    428             String packageName;
    429             boolean replacing;
    430             UserHandle user;
    431         }
    432 
    433         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
    434             super(looper, null, true);
    435             mCallback = callback;
    436         }
    437 
    438         @Override
    439         public void handleMessage(Message msg) {
    440             if (mCallback == null || !(msg.obj instanceof CallbackInfo)) {
    441                 return;
    442             }
    443             CallbackInfo info = (CallbackInfo) msg.obj;
    444             switch (msg.what) {
    445                 case MSG_ADDED:
    446                     mCallback.onPackageAdded(info.packageName, info.user);
    447                     break;
    448                 case MSG_REMOVED:
    449                     mCallback.onPackageRemoved(info.packageName, info.user);
    450                     break;
    451                 case MSG_CHANGED:
    452                     mCallback.onPackageChanged(info.packageName, info.user);
    453                     break;
    454                 case MSG_AVAILABLE:
    455                     mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing);
    456                     break;
    457                 case MSG_UNAVAILABLE:
    458                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
    459                     break;
    460             }
    461         }
    462 
    463         public void postOnPackageAdded(String packageName, UserHandle user) {
    464             CallbackInfo info = new CallbackInfo();
    465             info.packageName = packageName;
    466             info.user = user;
    467             obtainMessage(MSG_ADDED, info).sendToTarget();
    468         }
    469 
    470         public void postOnPackageRemoved(String packageName, UserHandle user) {
    471             CallbackInfo info = new CallbackInfo();
    472             info.packageName = packageName;
    473             info.user = user;
    474             obtainMessage(MSG_REMOVED, info).sendToTarget();
    475         }
    476 
    477         public void postOnPackageChanged(String packageName, UserHandle user) {
    478             CallbackInfo info = new CallbackInfo();
    479             info.packageName = packageName;
    480             info.user = user;
    481             obtainMessage(MSG_CHANGED, info).sendToTarget();
    482         }
    483 
    484         public void postOnPackagesAvailable(String[] packageNames, UserHandle user,
    485                 boolean replacing) {
    486             CallbackInfo info = new CallbackInfo();
    487             info.packageNames = packageNames;
    488             info.replacing = replacing;
    489             info.user = user;
    490             obtainMessage(MSG_AVAILABLE, info).sendToTarget();
    491         }
    492 
    493         public void postOnPackagesUnavailable(String[] packageNames, UserHandle user,
    494                 boolean replacing) {
    495             CallbackInfo info = new CallbackInfo();
    496             info.packageNames = packageNames;
    497             info.replacing = replacing;
    498             info.user = user;
    499             obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
    500         }
    501     }
    502 }
    503