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