LauncherAppsService.java revision 83f6d2da372de339dc563d6a7786be3facc52e76
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 com.android.server.pm; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.annotation.UserIdInt; 22import android.app.ActivityManager; 23import android.app.ActivityManagerInternal; 24import android.app.AppGlobals; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.pm.ActivityInfo; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.ILauncherApps; 31import android.content.pm.IOnAppsChangedListener; 32import android.content.pm.IPackageManager; 33import android.content.pm.LauncherApps; 34import android.content.pm.LauncherApps.ShortcutQuery; 35import android.content.pm.PackageInfo; 36import android.content.pm.PackageManager; 37import android.content.pm.PackageManager.NameNotFoundException; 38import android.content.pm.ParceledListSlice; 39import android.content.pm.ResolveInfo; 40import android.content.pm.ShortcutInfo; 41import android.content.pm.ShortcutServiceInternal; 42import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 43import android.content.pm.UserInfo; 44import android.graphics.Rect; 45import android.net.Uri; 46import android.os.Binder; 47import android.os.Bundle; 48import android.os.Handler; 49import android.os.IInterface; 50import android.os.ParcelFileDescriptor; 51import android.os.RemoteCallbackList; 52import android.os.RemoteException; 53import android.os.UserHandle; 54import android.os.UserManager; 55import android.provider.Settings; 56import android.util.Log; 57import android.util.Slog; 58 59import com.android.internal.annotations.VisibleForTesting; 60import com.android.internal.content.PackageMonitor; 61import com.android.internal.os.BackgroundThread; 62import com.android.internal.util.Preconditions; 63import com.android.server.LocalServices; 64import com.android.server.SystemService; 65 66import java.util.ArrayList; 67import java.util.List; 68 69/** 70 * Service that manages requests and callbacks for launchers that support 71 * managed profiles. 72 */ 73public class LauncherAppsService extends SystemService { 74 75 private final LauncherAppsImpl mLauncherAppsImpl; 76 77 public LauncherAppsService(Context context) { 78 super(context); 79 mLauncherAppsImpl = new LauncherAppsImpl(context); 80 } 81 82 @Override 83 public void onStart() { 84 publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl); 85 } 86 87 static class BroadcastCookie { 88 public final UserHandle user; 89 public final String packageName; 90 91 BroadcastCookie(UserHandle userHandle, String packageName) { 92 this.user = userHandle; 93 this.packageName = packageName; 94 } 95 } 96 97 @VisibleForTesting 98 static class LauncherAppsImpl extends ILauncherApps.Stub { 99 private static final boolean DEBUG = false; 100 private static final String TAG = "LauncherAppsService"; 101 private final Context mContext; 102 private final PackageManager mPm; 103 private final UserManager mUm; 104 private final ActivityManagerInternal mActivityManagerInternal; 105 private final ShortcutServiceInternal mShortcutServiceInternal; 106 private final PackageCallbackList<IOnAppsChangedListener> mListeners 107 = new PackageCallbackList<IOnAppsChangedListener>(); 108 109 private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); 110 111 private final Handler mCallbackHandler; 112 113 public LauncherAppsImpl(Context context) { 114 mContext = context; 115 mPm = mContext.getPackageManager(); 116 mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 117 mActivityManagerInternal = Preconditions.checkNotNull( 118 LocalServices.getService(ActivityManagerInternal.class)); 119 mShortcutServiceInternal = Preconditions.checkNotNull( 120 LocalServices.getService(ShortcutServiceInternal.class)); 121 mShortcutServiceInternal.addListener(mPackageMonitor); 122 mCallbackHandler = BackgroundThread.getHandler(); 123 } 124 125 @VisibleForTesting 126 int injectBinderCallingUid() { 127 return getCallingUid(); 128 } 129 130 final int injectCallingUserId() { 131 return UserHandle.getUserId(injectBinderCallingUid()); 132 } 133 134 @VisibleForTesting 135 long injectClearCallingIdentity() { 136 return Binder.clearCallingIdentity(); 137 } 138 139 // Injection point. 140 @VisibleForTesting 141 void injectRestoreCallingIdentity(long token) { 142 Binder.restoreCallingIdentity(token); 143 } 144 145 private int getCallingUserId() { 146 return UserHandle.getUserId(injectBinderCallingUid()); 147 } 148 149 /* 150 * @see android.content.pm.ILauncherApps#addOnAppsChangedListener( 151 * android.content.pm.IOnAppsChangedListener) 152 */ 153 @Override 154 public void addOnAppsChangedListener(String callingPackage, IOnAppsChangedListener listener) 155 throws RemoteException { 156 verifyCallingPackage(callingPackage); 157 synchronized (mListeners) { 158 if (DEBUG) { 159 Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle()); 160 } 161 if (mListeners.getRegisteredCallbackCount() == 0) { 162 if (DEBUG) { 163 Log.d(TAG, "Starting package monitoring"); 164 } 165 startWatchingPackageBroadcasts(); 166 } 167 mListeners.unregister(listener); 168 mListeners.register(listener, new BroadcastCookie(UserHandle.of(getCallingUserId()), 169 callingPackage)); 170 } 171 } 172 173 /* 174 * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener( 175 * android.content.pm.IOnAppsChangedListener) 176 */ 177 @Override 178 public void removeOnAppsChangedListener(IOnAppsChangedListener listener) 179 throws RemoteException { 180 synchronized (mListeners) { 181 if (DEBUG) { 182 Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle()); 183 } 184 mListeners.unregister(listener); 185 if (mListeners.getRegisteredCallbackCount() == 0) { 186 stopWatchingPackageBroadcasts(); 187 } 188 } 189 } 190 191 /** 192 * Register a receiver to watch for package broadcasts 193 */ 194 private void startWatchingPackageBroadcasts() { 195 mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler); 196 } 197 198 /** 199 * Unregister package broadcast receiver 200 */ 201 private void stopWatchingPackageBroadcasts() { 202 if (DEBUG) { 203 Log.d(TAG, "Stopped watching for packages"); 204 } 205 mPackageMonitor.unregister(); 206 } 207 208 void checkCallbackCount() { 209 synchronized (mListeners) { 210 if (DEBUG) { 211 Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount()); 212 } 213 if (mListeners.getRegisteredCallbackCount() == 0) { 214 stopWatchingPackageBroadcasts(); 215 } 216 } 217 } 218 219 /** 220 * Checks if the caller is in the same group as the userToCheck. 221 */ 222 private void ensureInUserProfiles(UserHandle userToCheck, String message) { 223 ensureInUserProfiles(userToCheck.getIdentifier(), message); 224 } 225 226 private void ensureInUserProfiles(int targetUserId, String message) { 227 final int callingUserId = injectCallingUserId(); 228 229 if (targetUserId == callingUserId) return; 230 231 long ident = injectClearCallingIdentity(); 232 try { 233 UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); 234 UserInfo targetUserInfo = mUm.getUserInfo(targetUserId); 235 if (targetUserInfo == null 236 || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID 237 || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) { 238 throw new SecurityException(message); 239 } 240 } finally { 241 injectRestoreCallingIdentity(ident); 242 } 243 } 244 245 @VisibleForTesting // We override it in unit tests 246 void verifyCallingPackage(String callingPackage) { 247 int packageUid = -1; 248 try { 249 packageUid = mPm.getPackageUidAsUser(callingPackage, 250 PackageManager.MATCH_DIRECT_BOOT_AWARE 251 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 252 | PackageManager.MATCH_UNINSTALLED_PACKAGES, 253 UserHandle.getUserId(getCallingUid())); 254 } catch (NameNotFoundException e) { 255 Log.e(TAG, "Package not found: " + callingPackage); 256 } 257 if (packageUid != Binder.getCallingUid()) { 258 throw new SecurityException("Calling package name mismatch"); 259 } 260 } 261 262 /** 263 * Checks if the user is enabled. 264 */ 265 private boolean isUserEnabled(UserHandle user) { 266 return isUserEnabled(user.getIdentifier()); 267 } 268 269 private boolean isUserEnabled(int userId) { 270 long ident = injectClearCallingIdentity(); 271 try { 272 UserInfo targetUserInfo = mUm.getUserInfo(userId); 273 return targetUserInfo != null && targetUserInfo.isEnabled(); 274 } finally { 275 injectRestoreCallingIdentity(ident); 276 } 277 } 278 279 @Override 280 public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) 281 throws RemoteException { 282 ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); 283 if (!isUserEnabled(user)) { 284 return null; 285 } 286 287 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 288 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 289 mainIntent.setPackage(packageName); 290 long ident = Binder.clearCallingIdentity(); 291 try { 292 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 293 PackageManager.MATCH_DIRECT_BOOT_AWARE 294 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 295 user.getIdentifier()); 296 return new ParceledListSlice<>(apps); 297 } finally { 298 Binder.restoreCallingIdentity(ident); 299 } 300 } 301 302 @Override 303 public ActivityInfo resolveActivity(ComponentName component, UserHandle user) 304 throws RemoteException { 305 ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user); 306 if (!isUserEnabled(user)) { 307 return null; 308 } 309 310 long ident = Binder.clearCallingIdentity(); 311 try { 312 IPackageManager pm = AppGlobals.getPackageManager(); 313 return pm.getActivityInfo(component, 314 PackageManager.MATCH_DIRECT_BOOT_AWARE 315 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 316 user.getIdentifier()); 317 } finally { 318 Binder.restoreCallingIdentity(ident); 319 } 320 } 321 322 @Override 323 public boolean isPackageEnabled(String packageName, UserHandle user) 324 throws RemoteException { 325 ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); 326 if (!isUserEnabled(user)) { 327 return false; 328 } 329 330 long ident = Binder.clearCallingIdentity(); 331 try { 332 IPackageManager pm = AppGlobals.getPackageManager(); 333 PackageInfo info = pm.getPackageInfo(packageName, 334 PackageManager.MATCH_DIRECT_BOOT_AWARE 335 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 336 user.getIdentifier()); 337 return info != null && info.applicationInfo.enabled; 338 } finally { 339 Binder.restoreCallingIdentity(ident); 340 } 341 } 342 343 @Override 344 public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) 345 throws RemoteException { 346 ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); 347 if (!isUserEnabled(user)) { 348 return null; 349 } 350 351 long ident = Binder.clearCallingIdentity(); 352 try { 353 IPackageManager pm = AppGlobals.getPackageManager(); 354 ApplicationInfo info = pm.getApplicationInfo(packageName, flags, 355 user.getIdentifier()); 356 return info; 357 } finally { 358 Binder.restoreCallingIdentity(ident); 359 } 360 } 361 362 private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) { 363 ensureShortcutPermission(callingPackage, user.getIdentifier()); 364 } 365 366 private void ensureShortcutPermission(@NonNull String callingPackage, int userId) { 367 verifyCallingPackage(callingPackage); 368 ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId); 369 370 if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(), 371 callingPackage)) { 372 throw new SecurityException("Caller can't access shortcut information"); 373 } 374 } 375 376 @Override 377 public ParceledListSlice getShortcuts(String callingPackage, long changedSince, 378 String packageName, List shortcutIds, ComponentName componentName, int flags, 379 UserHandle user) { 380 ensureShortcutPermission(callingPackage, user); 381 if (!isUserEnabled(user)) { 382 return new ParceledListSlice<>(new ArrayList(0)); 383 } 384 if (shortcutIds != null && packageName == null) { 385 throw new IllegalArgumentException( 386 "To query by shortcut ID, package name must also be set"); 387 } 388 389 // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below. 390 return new ParceledListSlice<>((List<ShortcutInfo>) 391 mShortcutServiceInternal.getShortcuts(getCallingUserId(), 392 callingPackage, changedSince, packageName, shortcutIds, 393 componentName, flags, user.getIdentifier())); 394 } 395 396 @Override 397 public void pinShortcuts(String callingPackage, String packageName, List<String> ids, 398 UserHandle user) { 399 ensureShortcutPermission(callingPackage, user); 400 if (!isUserEnabled(user)) { 401 throw new IllegalStateException("Cannot pin shortcuts for disabled profile " 402 + user); 403 } 404 405 mShortcutServiceInternal.pinShortcuts(getCallingUserId(), 406 callingPackage, packageName, ids, user.getIdentifier()); 407 } 408 409 @Override 410 public int getShortcutIconResId(String callingPackage, String packageName, String id, 411 int userId) { 412 ensureShortcutPermission(callingPackage, userId); 413 if (!isUserEnabled(userId)) { 414 return 0; 415 } 416 417 return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(), 418 callingPackage, packageName, id, userId); 419 } 420 421 @Override 422 public ParcelFileDescriptor getShortcutIconFd(String callingPackage, 423 String packageName, String id, int userId) { 424 ensureShortcutPermission(callingPackage, userId); 425 if (!isUserEnabled(userId)) { 426 return null; 427 } 428 429 return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(), 430 callingPackage, packageName, id, userId); 431 } 432 433 @Override 434 public boolean hasShortcutHostPermission(String callingPackage) { 435 verifyCallingPackage(callingPackage); 436 return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(), 437 callingPackage); 438 } 439 440 @Override 441 public boolean startShortcut(String callingPackage, String packageName, String shortcutId, 442 Rect sourceBounds, Bundle startActivityOptions, int userId) { 443 verifyCallingPackage(callingPackage); 444 ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId); 445 446 if (!isUserEnabled(userId)) { 447 throw new IllegalStateException("Cannot start a shortcut for disabled profile " 448 + userId); 449 } 450 451 // Even without the permission, pinned shortcuts are always launchable. 452 if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(), 453 callingPackage, packageName, shortcutId, userId)) { 454 ensureShortcutPermission(callingPackage, userId); 455 } 456 457 final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(), 458 callingPackage, packageName, shortcutId, userId); 459 if (intent == null) { 460 return false; 461 } 462 // Note the target activity doesn't have to be exported. 463 464 prepareIntentForLaunch(intent, sourceBounds); 465 466 return startShortcutIntentAsPublisher( 467 intent, packageName, startActivityOptions, userId); 468 } 469 470 private boolean startShortcutIntentAsPublisher(@NonNull Intent intent, 471 @NonNull String publisherPackage, Bundle startActivityOptions, int userId) { 472 final int code; 473 final long ident = injectClearCallingIdentity(); 474 try { 475 code = mActivityManagerInternal.startActivityAsPackage(publisherPackage, 476 userId, intent, startActivityOptions); 477 if (code >= ActivityManager.START_SUCCESS) { 478 return true; // Success 479 } else { 480 Log.e(TAG, "Couldn't start activity, code=" + code); 481 } 482 return code >= ActivityManager.START_SUCCESS; 483 } catch (SecurityException e) { 484 if (DEBUG) { 485 Slog.d(TAG, "SecurityException while launching intent", e); 486 } 487 return false; 488 } finally { 489 injectRestoreCallingIdentity(ident); 490 } 491 } 492 493 @Override 494 public boolean isActivityEnabled(ComponentName component, UserHandle user) 495 throws RemoteException { 496 ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); 497 if (!isUserEnabled(user)) { 498 return false; 499 } 500 501 long ident = Binder.clearCallingIdentity(); 502 try { 503 IPackageManager pm = AppGlobals.getPackageManager(); 504 ActivityInfo info = pm.getActivityInfo(component, 505 PackageManager.MATCH_DIRECT_BOOT_AWARE 506 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 507 user.getIdentifier()); 508 return info != null; 509 } finally { 510 Binder.restoreCallingIdentity(ident); 511 } 512 } 513 514 @Override 515 public void startActivityAsUser(ComponentName component, Rect sourceBounds, 516 Bundle opts, UserHandle user) throws RemoteException { 517 ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); 518 if (!isUserEnabled(user)) { 519 throw new IllegalStateException("Cannot start activity for disabled profile " + user); 520 } 521 522 Intent launchIntent = new Intent(Intent.ACTION_MAIN); 523 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 524 prepareIntentForLaunch(launchIntent, sourceBounds); 525 launchIntent.setPackage(component.getPackageName()); 526 527 long ident = Binder.clearCallingIdentity(); 528 try { 529 IPackageManager pm = AppGlobals.getPackageManager(); 530 ActivityInfo info = pm.getActivityInfo(component, 531 PackageManager.MATCH_DIRECT_BOOT_AWARE 532 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 533 user.getIdentifier()); 534 if (!info.exported) { 535 throw new SecurityException("Cannot launch non-exported components " 536 + component); 537 } 538 539 // Check that the component actually has Intent.CATEGORY_LAUCNCHER 540 // as calling startActivityAsUser ignores the category and just 541 // resolves based on the component if present. 542 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent, 543 PackageManager.MATCH_DIRECT_BOOT_AWARE 544 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 545 user.getIdentifier()); 546 final int size = apps.size(); 547 for (int i = 0; i < size; ++i) { 548 ActivityInfo activityInfo = apps.get(i).activityInfo; 549 if (activityInfo.packageName.equals(component.getPackageName()) && 550 activityInfo.name.equals(component.getClassName())) { 551 // Found an activity with category launcher that matches 552 // this component so ok to launch. 553 launchIntent.setComponent(component); 554 mContext.startActivityAsUser(launchIntent, opts, user); 555 return; 556 } 557 } 558 throw new SecurityException("Attempt to launch activity without " 559 + " category Intent.CATEGORY_LAUNCHER " + component); 560 } finally { 561 Binder.restoreCallingIdentity(ident); 562 } 563 } 564 565 private void prepareIntentForLaunch(@NonNull Intent launchIntent, 566 @Nullable Rect sourceBounds) { 567 launchIntent.setSourceBounds(sourceBounds); 568 launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK 569 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 570 } 571 572 @Override 573 public void showAppDetailsAsUser(ComponentName component, Rect sourceBounds, 574 Bundle opts, UserHandle user) throws RemoteException { 575 ensureInUserProfiles(user, "Cannot show app details for unrelated profile " + user); 576 if (!isUserEnabled(user)) { 577 throw new IllegalStateException("Cannot show app details for disabled profile " 578 + user); 579 } 580 581 long ident = Binder.clearCallingIdentity(); 582 try { 583 String packageName = component.getPackageName(); 584 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 585 Uri.fromParts("package", packageName, null)); 586 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 587 intent.setSourceBounds(sourceBounds); 588 mContext.startActivityAsUser(intent, opts, user); 589 } finally { 590 Binder.restoreCallingIdentity(ident); 591 } 592 } 593 594 /** Checks if user is a profile of or same as listeningUser. 595 * and the user is enabled. */ 596 private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, 597 String debugMsg) { 598 if (user.getIdentifier() == listeningUser.getIdentifier()) { 599 if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); 600 return true; 601 } 602 long ident = injectClearCallingIdentity(); 603 try { 604 UserInfo userInfo = mUm.getUserInfo(user.getIdentifier()); 605 UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); 606 if (userInfo == null || listeningUserInfo == null 607 || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID 608 || userInfo.profileGroupId != listeningUserInfo.profileGroupId 609 || !userInfo.isEnabled()) { 610 if (DEBUG) { 611 Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" 612 + debugMsg); 613 } 614 return false; 615 } else { 616 if (DEBUG) { 617 Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":" 618 + debugMsg); 619 } 620 return true; 621 } 622 } finally { 623 injectRestoreCallingIdentity(ident); 624 } 625 } 626 627 @VisibleForTesting 628 void postToPackageMonitorHandler(Runnable r) { 629 mCallbackHandler.post(r); 630 } 631 632 private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener { 633 634 // TODO Simplify with lambdas. 635 636 @Override 637 public void onPackageAdded(String packageName, int uid) { 638 UserHandle user = new UserHandle(getChangingUserId()); 639 final int n = mListeners.beginBroadcast(); 640 for (int i = 0; i < n; i++) { 641 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 642 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 643 if (!isEnabledProfileOf(user, cookie.user, "onPackageAdded")) continue; 644 try { 645 listener.onPackageAdded(user, packageName); 646 } catch (RemoteException re) { 647 Slog.d(TAG, "Callback failed ", re); 648 } 649 } 650 mListeners.finishBroadcast(); 651 652 super.onPackageAdded(packageName, uid); 653 } 654 655 @Override 656 public void onPackageRemoved(String packageName, int uid) { 657 UserHandle user = new UserHandle(getChangingUserId()); 658 final int n = mListeners.beginBroadcast(); 659 for (int i = 0; i < n; i++) { 660 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 661 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 662 if (!isEnabledProfileOf(user, cookie.user, "onPackageRemoved")) continue; 663 try { 664 listener.onPackageRemoved(user, packageName); 665 } catch (RemoteException re) { 666 Slog.d(TAG, "Callback failed ", re); 667 } 668 } 669 mListeners.finishBroadcast(); 670 671 super.onPackageRemoved(packageName, uid); 672 } 673 674 @Override 675 public void onPackageModified(String packageName) { 676 UserHandle user = new UserHandle(getChangingUserId()); 677 final int n = mListeners.beginBroadcast(); 678 for (int i = 0; i < n; i++) { 679 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 680 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 681 if (!isEnabledProfileOf(user, cookie.user, "onPackageModified")) continue; 682 try { 683 listener.onPackageChanged(user, packageName); 684 } catch (RemoteException re) { 685 Slog.d(TAG, "Callback failed ", re); 686 } 687 } 688 mListeners.finishBroadcast(); 689 690 super.onPackageModified(packageName); 691 } 692 693 @Override 694 public void onPackagesAvailable(String[] packages) { 695 UserHandle user = new UserHandle(getChangingUserId()); 696 final int n = mListeners.beginBroadcast(); 697 for (int i = 0; i < n; i++) { 698 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 699 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 700 if (!isEnabledProfileOf(user, cookie.user, "onPackagesAvailable")) continue; 701 try { 702 listener.onPackagesAvailable(user, packages, isReplacing()); 703 } catch (RemoteException re) { 704 Slog.d(TAG, "Callback failed ", re); 705 } 706 } 707 mListeners.finishBroadcast(); 708 709 super.onPackagesAvailable(packages); 710 } 711 712 @Override 713 public void onPackagesUnavailable(String[] packages) { 714 UserHandle user = new UserHandle(getChangingUserId()); 715 final int n = mListeners.beginBroadcast(); 716 for (int i = 0; i < n; i++) { 717 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 718 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 719 if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnavailable")) continue; 720 try { 721 listener.onPackagesUnavailable(user, packages, isReplacing()); 722 } catch (RemoteException re) { 723 Slog.d(TAG, "Callback failed ", re); 724 } 725 } 726 mListeners.finishBroadcast(); 727 728 super.onPackagesUnavailable(packages); 729 } 730 731 @Override 732 public void onPackagesSuspended(String[] packages) { 733 UserHandle user = new UserHandle(getChangingUserId()); 734 final int n = mListeners.beginBroadcast(); 735 for (int i = 0; i < n; i++) { 736 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 737 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 738 if (!isEnabledProfileOf(user, cookie.user, "onPackagesSuspended")) continue; 739 try { 740 listener.onPackagesSuspended(user, packages); 741 } catch (RemoteException re) { 742 Slog.d(TAG, "Callback failed ", re); 743 } 744 } 745 mListeners.finishBroadcast(); 746 747 super.onPackagesSuspended(packages); 748 } 749 750 @Override 751 public void onPackagesUnsuspended(String[] packages) { 752 UserHandle user = new UserHandle(getChangingUserId()); 753 final int n = mListeners.beginBroadcast(); 754 for (int i = 0; i < n; i++) { 755 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 756 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 757 if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnsuspended")) continue; 758 try { 759 listener.onPackagesUnsuspended(user, packages); 760 } catch (RemoteException re) { 761 Slog.d(TAG, "Callback failed ", re); 762 } 763 } 764 mListeners.finishBroadcast(); 765 766 super.onPackagesUnsuspended(packages); 767 } 768 769 @Override 770 public void onShortcutChanged(@NonNull String packageName, 771 @UserIdInt int userId) { 772 postToPackageMonitorHandler(() -> onShortcutChangedInner(packageName, userId)); 773 } 774 775 private void onShortcutChangedInner(@NonNull String packageName, 776 @UserIdInt int userId) { 777 final UserHandle user = UserHandle.of(userId); 778 779 final int n = mListeners.beginBroadcast(); 780 for (int i = 0; i < n; i++) { 781 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 782 BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); 783 if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue; 784 785 final int launcherUserId = cookie.user.getIdentifier(); 786 787 // Make sure the caller has the permission. 788 if (!mShortcutServiceInternal.hasShortcutHostPermission( 789 launcherUserId, cookie.packageName)) { 790 continue; 791 } 792 // Each launcher has a different set of pinned shortcuts, so we need to do a 793 // query in here. 794 // (As of now, only one launcher has the permission at a time, so it's bit 795 // moot, but we may change the permission model eventually.) 796 final List<ShortcutInfo> list = 797 mShortcutServiceInternal.getShortcuts(launcherUserId, 798 cookie.packageName, 799 /* changedSince= */ 0, packageName, /* shortcutIds=*/ null, 800 /* component= */ null, 801 ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY 802 | ShortcutQuery.FLAG_GET_ALL_KINDS 803 , userId); 804 try { 805 listener.onShortcutChanged(user, packageName, 806 new ParceledListSlice<>(list)); 807 } catch (RemoteException re) { 808 Slog.d(TAG, "Callback failed ", re); 809 } 810 } 811 mListeners.finishBroadcast(); 812 } 813 } 814 815 class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> { 816 @Override 817 public void onCallbackDied(T callback, Object cookie) { 818 checkCallbackCount(); 819 } 820 } 821 } 822} 823