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