LauncherAppsService.java revision 7a6a05f0ccc8c57496d0a1e2b0882ab7de3175e5
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.AppGlobals; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.pm.ActivityInfo; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.ILauncherApps; 29import android.content.pm.IOnAppsChangedListener; 30import android.content.pm.IPackageManager; 31import android.content.pm.PackageInfo; 32import android.content.pm.PackageManager; 33import android.content.pm.PackageManager.NameNotFoundException; 34import android.content.pm.ParceledListSlice; 35import android.content.pm.ResolveInfo; 36import android.content.pm.ShortcutInfo; 37import android.content.pm.ShortcutServiceInternal; 38import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 39import android.content.pm.UserInfo; 40import android.graphics.Rect; 41import android.net.Uri; 42import android.os.Binder; 43import android.os.Bundle; 44import android.os.IInterface; 45import android.os.ParcelFileDescriptor; 46import android.os.RemoteCallbackList; 47import android.os.RemoteException; 48import android.os.UserHandle; 49import android.os.UserManager; 50import android.provider.Settings; 51import android.util.Log; 52import android.util.Slog; 53 54import com.android.internal.annotations.VisibleForTesting; 55import com.android.internal.content.PackageMonitor; 56import com.android.internal.util.Preconditions; 57import com.android.server.LocalServices; 58import com.android.server.SystemService; 59 60import java.util.List; 61 62/** 63 * Service that manages requests and callbacks for launchers that support 64 * managed profiles. 65 */ 66public class LauncherAppsService extends SystemService { 67 68 private final LauncherAppsImpl mLauncherAppsImpl; 69 70 public LauncherAppsService(Context context) { 71 super(context); 72 mLauncherAppsImpl = new LauncherAppsImpl(context); 73 } 74 75 @Override 76 public void onStart() { 77 publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl); 78 } 79 80 @VisibleForTesting 81 static class LauncherAppsImpl extends ILauncherApps.Stub { 82 private static final boolean DEBUG = false; 83 private static final String TAG = "LauncherAppsService"; 84 private final Context mContext; 85 private final PackageManager mPm; 86 private final UserManager mUm; 87 private final ShortcutServiceInternal mShortcutServiceInternal; 88 private final PackageCallbackList<IOnAppsChangedListener> mListeners 89 = new PackageCallbackList<IOnAppsChangedListener>(); 90 91 private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); 92 93 public LauncherAppsImpl(Context context) { 94 mContext = context; 95 mPm = mContext.getPackageManager(); 96 mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 97 mShortcutServiceInternal = Preconditions.checkNotNull( 98 LocalServices.getService(ShortcutServiceInternal.class)); 99 mShortcutServiceInternal.addListener(mPackageMonitor); 100 } 101 102 /* 103 * @see android.content.pm.ILauncherApps#addOnAppsChangedListener( 104 * android.content.pm.IOnAppsChangedListener) 105 */ 106 @Override 107 public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException { 108 synchronized (mListeners) { 109 if (DEBUG) { 110 Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle()); 111 } 112 if (mListeners.getRegisteredCallbackCount() == 0) { 113 if (DEBUG) { 114 Log.d(TAG, "Starting package monitoring"); 115 } 116 startWatchingPackageBroadcasts(); 117 } 118 mListeners.unregister(listener); 119 mListeners.register(listener, Binder.getCallingUserHandle()); 120 } 121 } 122 123 /* 124 * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener( 125 * android.content.pm.IOnAppsChangedListener) 126 */ 127 @Override 128 public void removeOnAppsChangedListener(IOnAppsChangedListener listener) 129 throws RemoteException { 130 synchronized (mListeners) { 131 if (DEBUG) { 132 Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle()); 133 } 134 mListeners.unregister(listener); 135 if (mListeners.getRegisteredCallbackCount() == 0) { 136 stopWatchingPackageBroadcasts(); 137 } 138 } 139 } 140 141 /** 142 * Register a receiver to watch for package broadcasts 143 */ 144 private void startWatchingPackageBroadcasts() { 145 mPackageMonitor.register(mContext, null, UserHandle.ALL, true); 146 } 147 148 /** 149 * Unregister package broadcast receiver 150 */ 151 private void stopWatchingPackageBroadcasts() { 152 if (DEBUG) { 153 Log.d(TAG, "Stopped watching for packages"); 154 } 155 mPackageMonitor.unregister(); 156 } 157 158 void checkCallbackCount() { 159 synchronized (mListeners) { 160 if (DEBUG) { 161 Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount()); 162 } 163 if (mListeners.getRegisteredCallbackCount() == 0) { 164 stopWatchingPackageBroadcasts(); 165 } 166 } 167 } 168 169 /** 170 * Checks if the caller is in the same group as the userToCheck. 171 */ 172 @VisibleForTesting // We override it in unit tests 173 void ensureInUserProfiles(UserHandle userToCheck, String message) { 174 final int callingUserId = UserHandle.getCallingUserId(); 175 final int targetUserId = userToCheck.getIdentifier(); 176 177 if (targetUserId == callingUserId) return; 178 179 long ident = Binder.clearCallingIdentity(); 180 try { 181 UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); 182 UserInfo targetUserInfo = mUm.getUserInfo(targetUserId); 183 if (targetUserInfo == null 184 || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID 185 || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) { 186 throw new SecurityException(message); 187 } 188 } finally { 189 Binder.restoreCallingIdentity(ident); 190 } 191 } 192 193 @VisibleForTesting // We override it in unit tests 194 void verifyCallingPackage(String callingPackage) { 195 int packageUid = -1; 196 try { 197 packageUid = mPm.getPackageUid(callingPackage, 198 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE 199 | PackageManager.MATCH_UNINSTALLED_PACKAGES); 200 } catch (NameNotFoundException e) { 201 Log.e(TAG, "Package not found: " + callingPackage); 202 } 203 if (packageUid != Binder.getCallingUid()) { 204 throw new SecurityException("Calling package name mismatch"); 205 } 206 } 207 208 /** 209 * Checks if the user is enabled. 210 */ 211 private boolean isUserEnabled(UserHandle user) { 212 long ident = Binder.clearCallingIdentity(); 213 try { 214 UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier()); 215 return targetUserInfo != null && targetUserInfo.isEnabled(); 216 } finally { 217 Binder.restoreCallingIdentity(ident); 218 } 219 } 220 221 @Override 222 public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) 223 throws RemoteException { 224 ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); 225 if (!isUserEnabled(user)) { 226 return null; 227 } 228 229 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 230 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 231 mainIntent.setPackage(packageName); 232 long ident = Binder.clearCallingIdentity(); 233 try { 234 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 235 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 236 return new ParceledListSlice<>(apps); 237 } finally { 238 Binder.restoreCallingIdentity(ident); 239 } 240 } 241 242 @Override 243 public ResolveInfo resolveActivity(Intent intent, UserHandle user) 244 throws RemoteException { 245 ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user); 246 if (!isUserEnabled(user)) { 247 return null; 248 } 249 250 long ident = Binder.clearCallingIdentity(); 251 try { 252 ResolveInfo app = mPm.resolveActivityAsUser(intent, 253 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 254 return app; 255 } finally { 256 Binder.restoreCallingIdentity(ident); 257 } 258 } 259 260 @Override 261 public boolean isPackageEnabled(String packageName, UserHandle user) 262 throws RemoteException { 263 ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); 264 if (!isUserEnabled(user)) { 265 return false; 266 } 267 268 long ident = Binder.clearCallingIdentity(); 269 try { 270 IPackageManager pm = AppGlobals.getPackageManager(); 271 PackageInfo info = pm.getPackageInfo(packageName, 272 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 273 return info != null && info.applicationInfo.enabled; 274 } finally { 275 Binder.restoreCallingIdentity(ident); 276 } 277 } 278 279 @Override 280 public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) 281 throws RemoteException { 282 ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); 283 if (!isUserEnabled(user)) { 284 return null; 285 } 286 287 long ident = Binder.clearCallingIdentity(); 288 try { 289 IPackageManager pm = AppGlobals.getPackageManager(); 290 ApplicationInfo info = pm.getApplicationInfo(packageName, flags, 291 user.getIdentifier()); 292 return info; 293 } finally { 294 Binder.restoreCallingIdentity(ident); 295 } 296 } 297 298 private void enforceShortcutPermission(UserHandle user) { 299 ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); 300 // STOPSHIP Implement it 301 } 302 303 @Override 304 public ParceledListSlice getShortcuts(String callingPackage, long changedSince, 305 String packageName, ComponentName componentName, int flags, UserHandle user) 306 throws RemoteException { 307 enforceShortcutPermission(user); 308 verifyCallingPackage(callingPackage); 309 310 return new ParceledListSlice<>( 311 mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName, 312 componentName, flags, user.getIdentifier())); 313 } 314 315 @Override 316 public ParceledListSlice getShortcutInfo(String callingPackage, String packageName, 317 List<String> ids, UserHandle user) throws RemoteException { 318 enforceShortcutPermission(user); 319 verifyCallingPackage(callingPackage); 320 321 return new ParceledListSlice<>( 322 mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName, 323 ids, user.getIdentifier())); 324 } 325 326 @Override 327 public void pinShortcuts(String callingPackage, String packageName, List<String> ids, 328 UserHandle user) throws RemoteException { 329 enforceShortcutPermission(user); 330 verifyCallingPackage(callingPackage); 331 332 mShortcutServiceInternal.pinShortcuts(callingPackage, packageName, 333 ids, user.getIdentifier()); 334 } 335 336 @Override 337 public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut, 338 UserHandle user) { 339 enforceShortcutPermission(user); 340 verifyCallingPackage(callingPackage); 341 342 return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut, 343 user.getIdentifier()); 344 } 345 346 @Override 347 public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut, 348 UserHandle user) { 349 enforceShortcutPermission(user); 350 verifyCallingPackage(callingPackage); 351 352 return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut, 353 user.getIdentifier()); 354 } 355 356 @Override 357 public boolean startShortcut(String callingPackage, String packageName, String shortcutId, 358 Rect sourceBounds, Bundle startActivityOptions, UserHandle user) 359 throws RemoteException { 360 enforceShortcutPermission(user); 361 verifyCallingPackage(callingPackage); 362 363 final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage, 364 packageName, shortcutId, user.getIdentifier()); 365 if (intent == null) { 366 return false; 367 } 368 // Note the target activity doesn't have to be exported. 369 370 intent.setSourceBounds(sourceBounds); 371 prepareIntentForLaunch(intent, sourceBounds); 372 373 final long ident = Binder.clearCallingIdentity(); 374 try { 375 mContext.startActivityAsUser(intent, startActivityOptions, user); 376 } finally { 377 Binder.restoreCallingIdentity(ident); 378 } 379 return true; 380 } 381 382 @Override 383 public boolean isActivityEnabled(ComponentName component, UserHandle user) 384 throws RemoteException { 385 ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); 386 if (!isUserEnabled(user)) { 387 return false; 388 } 389 390 long ident = Binder.clearCallingIdentity(); 391 try { 392 IPackageManager pm = AppGlobals.getPackageManager(); 393 ActivityInfo info = pm.getActivityInfo(component, 394 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 395 return info != null; 396 } finally { 397 Binder.restoreCallingIdentity(ident); 398 } 399 } 400 401 @Override 402 public void startActivityAsUser(ComponentName component, Rect sourceBounds, 403 Bundle opts, UserHandle user) throws RemoteException { 404 ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); 405 if (!isUserEnabled(user)) { 406 throw new IllegalStateException("Cannot start activity for disabled profile " + user); 407 } 408 409 Intent launchIntent = new Intent(Intent.ACTION_MAIN); 410 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 411 prepareIntentForLaunch(launchIntent, sourceBounds); 412 launchIntent.setPackage(component.getPackageName()); 413 414 long ident = Binder.clearCallingIdentity(); 415 try { 416 IPackageManager pm = AppGlobals.getPackageManager(); 417 ActivityInfo info = pm.getActivityInfo(component, 418 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 419 if (!info.exported) { 420 throw new SecurityException("Cannot launch non-exported components " 421 + component); 422 } 423 424 // Check that the component actually has Intent.CATEGORY_LAUCNCHER 425 // as calling startActivityAsUser ignores the category and just 426 // resolves based on the component if present. 427 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent, 428 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier()); 429 final int size = apps.size(); 430 for (int i = 0; i < size; ++i) { 431 ActivityInfo activityInfo = apps.get(i).activityInfo; 432 if (activityInfo.packageName.equals(component.getPackageName()) && 433 activityInfo.name.equals(component.getClassName())) { 434 // Found an activity with category launcher that matches 435 // this component so ok to launch. 436 launchIntent.setComponent(component); 437 mContext.startActivityAsUser(launchIntent, opts, user); 438 return; 439 } 440 } 441 throw new SecurityException("Attempt to launch activity without " 442 + " category Intent.CATEGORY_LAUNCHER " + component); 443 } finally { 444 Binder.restoreCallingIdentity(ident); 445 } 446 } 447 448 private void prepareIntentForLaunch(@NonNull Intent launchIntent, 449 @Nullable Rect sourceBounds) { 450 launchIntent.setSourceBounds(sourceBounds); 451 launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK 452 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 453 } 454 455 @Override 456 public void showAppDetailsAsUser(ComponentName component, Rect sourceBounds, 457 Bundle opts, UserHandle user) throws RemoteException { 458 ensureInUserProfiles(user, "Cannot show app details for unrelated profile " + user); 459 if (!isUserEnabled(user)) { 460 throw new IllegalStateException("Cannot show app details for disabled profile " 461 + user); 462 } 463 464 long ident = Binder.clearCallingIdentity(); 465 try { 466 String packageName = component.getPackageName(); 467 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 468 Uri.fromParts("package", packageName, null)); 469 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 470 intent.setSourceBounds(sourceBounds); 471 mContext.startActivityAsUser(intent, opts, user); 472 } finally { 473 Binder.restoreCallingIdentity(ident); 474 } 475 } 476 477 478 private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener { 479 480 /** Checks if user is a profile of or same as listeningUser. 481 * and the user is enabled. */ 482 private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, 483 String debugMsg) { 484 if (user.getIdentifier() == listeningUser.getIdentifier()) { 485 if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); 486 return true; 487 } 488 long ident = Binder.clearCallingIdentity(); 489 try { 490 UserInfo userInfo = mUm.getUserInfo(user.getIdentifier()); 491 UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); 492 if (userInfo == null || listeningUserInfo == null 493 || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID 494 || userInfo.profileGroupId != listeningUserInfo.profileGroupId 495 || !userInfo.isEnabled()) { 496 if (DEBUG) { 497 Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" 498 + debugMsg); 499 } 500 return false; 501 } else { 502 if (DEBUG) { 503 Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":" 504 + debugMsg); 505 } 506 return true; 507 } 508 } finally { 509 Binder.restoreCallingIdentity(ident); 510 } 511 } 512 513 // TODO Simplify with lambdas. 514 515 @Override 516 public void onPackageAdded(String packageName, int uid) { 517 UserHandle user = new UserHandle(getChangingUserId()); 518 final int n = mListeners.beginBroadcast(); 519 for (int i = 0; i < n; i++) { 520 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 521 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 522 if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue; 523 try { 524 listener.onPackageAdded(user, packageName); 525 } catch (RemoteException re) { 526 Slog.d(TAG, "Callback failed ", re); 527 } 528 } 529 mListeners.finishBroadcast(); 530 531 super.onPackageAdded(packageName, uid); 532 } 533 534 @Override 535 public void onPackageRemoved(String packageName, int uid) { 536 UserHandle user = new UserHandle(getChangingUserId()); 537 final int n = mListeners.beginBroadcast(); 538 for (int i = 0; i < n; i++) { 539 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 540 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 541 if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue; 542 try { 543 listener.onPackageRemoved(user, packageName); 544 } catch (RemoteException re) { 545 Slog.d(TAG, "Callback failed ", re); 546 } 547 } 548 mListeners.finishBroadcast(); 549 550 super.onPackageRemoved(packageName, uid); 551 } 552 553 @Override 554 public void onPackageModified(String packageName) { 555 UserHandle user = new UserHandle(getChangingUserId()); 556 final int n = mListeners.beginBroadcast(); 557 for (int i = 0; i < n; i++) { 558 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 559 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 560 if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue; 561 try { 562 listener.onPackageChanged(user, packageName); 563 } catch (RemoteException re) { 564 Slog.d(TAG, "Callback failed ", re); 565 } 566 } 567 mListeners.finishBroadcast(); 568 569 super.onPackageModified(packageName); 570 } 571 572 @Override 573 public void onPackagesAvailable(String[] packages) { 574 UserHandle user = new UserHandle(getChangingUserId()); 575 final int n = mListeners.beginBroadcast(); 576 for (int i = 0; i < n; i++) { 577 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 578 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 579 if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue; 580 try { 581 listener.onPackagesAvailable(user, packages, isReplacing()); 582 } catch (RemoteException re) { 583 Slog.d(TAG, "Callback failed ", re); 584 } 585 } 586 mListeners.finishBroadcast(); 587 588 super.onPackagesAvailable(packages); 589 } 590 591 @Override 592 public void onPackagesUnavailable(String[] packages) { 593 UserHandle user = new UserHandle(getChangingUserId()); 594 final int n = mListeners.beginBroadcast(); 595 for (int i = 0; i < n; i++) { 596 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 597 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 598 if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; 599 try { 600 listener.onPackagesUnavailable(user, packages, isReplacing()); 601 } catch (RemoteException re) { 602 Slog.d(TAG, "Callback failed ", re); 603 } 604 } 605 mListeners.finishBroadcast(); 606 607 super.onPackagesUnavailable(packages); 608 } 609 610 @Override 611 public void onPackagesSuspended(String[] packages) { 612 UserHandle user = new UserHandle(getChangingUserId()); 613 final int n = mListeners.beginBroadcast(); 614 for (int i = 0; i < n; i++) { 615 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 616 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 617 if (!isEnabledProfileOf(user, listeningUser, "onPackagesSuspended")) continue; 618 try { 619 listener.onPackagesSuspended(user, packages); 620 } catch (RemoteException re) { 621 Slog.d(TAG, "Callback failed ", re); 622 } 623 } 624 mListeners.finishBroadcast(); 625 626 super.onPackagesSuspended(packages); 627 } 628 629 @Override 630 public void onPackagesUnsuspended(String[] packages) { 631 UserHandle user = new UserHandle(getChangingUserId()); 632 final int n = mListeners.beginBroadcast(); 633 for (int i = 0; i < n; i++) { 634 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 635 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 636 if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnsuspended")) continue; 637 try { 638 listener.onPackagesUnsuspended(user, packages); 639 } catch (RemoteException re) { 640 Slog.d(TAG, "Callback failed ", re); 641 } 642 } 643 mListeners.finishBroadcast(); 644 645 super.onPackagesUnsuspended(packages); 646 } 647 648 @Override 649 public void onShortcutChanged(@NonNull String packageName, 650 @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId) { 651 final UserHandle user = UserHandle.of(userId); 652 653 final int n = mListeners.beginBroadcast(); 654 for (int i = 0; i < n; i++) { 655 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); 656 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); 657 if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue; 658 try { 659 listener.onShortcutChanged(user, packageName, 660 new ParceledListSlice<>(shortcuts)); 661 } catch (RemoteException re) { 662 Slog.d(TAG, "Callback failed ", re); 663 } 664 } 665 mListeners.finishBroadcast(); 666 } 667 } 668 669 class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> { 670 @Override 671 public void onCallbackDied(T callback, Object cookie) { 672 checkCallbackCount(); 673 } 674 } 675 } 676} 677