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