LauncherAppsService.java revision 3ee7f71d5f1dc018fdafee9d890290909ad299df
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.getPackageUidAsUser(callingPackage,
194                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
195                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
196                        UserHandle.getUserId(getCallingUid()));
197            } catch (NameNotFoundException e) {
198                Log.e(TAG, "Package not found: " + callingPackage);
199            }
200            if (packageUid != Binder.getCallingUid()) {
201                throw new SecurityException("Calling package name mismatch");
202            }
203        }
204
205        /**
206         * Checks if the user is enabled.
207         */
208        private boolean isUserEnabled(UserHandle user) {
209            long ident = Binder.clearCallingIdentity();
210            try {
211                UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
212                return targetUserInfo != null && targetUserInfo.isEnabled();
213            } finally {
214                Binder.restoreCallingIdentity(ident);
215            }
216        }
217
218        @Override
219        public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
220                throws RemoteException {
221            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
222            if (!isUserEnabled(user)) {
223                return null;
224            }
225
226            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
227            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
228            mainIntent.setPackage(packageName);
229            long ident = Binder.clearCallingIdentity();
230            try {
231                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
232                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
233                return new ParceledListSlice<>(apps);
234            } finally {
235                Binder.restoreCallingIdentity(ident);
236            }
237        }
238
239        @Override
240        public ResolveInfo resolveActivity(Intent intent, UserHandle user)
241                throws RemoteException {
242            ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
243            if (!isUserEnabled(user)) {
244                return null;
245            }
246
247            long ident = Binder.clearCallingIdentity();
248            try {
249                ResolveInfo app = mPm.resolveActivityAsUser(intent,
250                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
251                return app;
252            } finally {
253                Binder.restoreCallingIdentity(ident);
254            }
255        }
256
257        @Override
258        public boolean isPackageEnabled(String packageName, UserHandle user)
259                throws RemoteException {
260            ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
261            if (!isUserEnabled(user)) {
262                return false;
263            }
264
265            long ident = Binder.clearCallingIdentity();
266            try {
267                IPackageManager pm = AppGlobals.getPackageManager();
268                PackageInfo info = pm.getPackageInfo(packageName,
269                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
270                return info != null && info.applicationInfo.enabled;
271            } finally {
272                Binder.restoreCallingIdentity(ident);
273            }
274        }
275
276        @Override
277        public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
278                throws RemoteException {
279            ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
280            if (!isUserEnabled(user)) {
281                return null;
282            }
283
284            long ident = Binder.clearCallingIdentity();
285            try {
286                IPackageManager pm = AppGlobals.getPackageManager();
287                ApplicationInfo info = pm.getApplicationInfo(packageName, flags,
288                        user.getIdentifier());
289                return info;
290            } finally {
291                Binder.restoreCallingIdentity(ident);
292            }
293        }
294
295        private void enforceShortcutPermission(UserHandle user) {
296            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
297            // STOPSHIP Implement it
298        }
299
300        @Override
301        public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
302                String packageName, ComponentName componentName, int flags, UserHandle user)
303                throws RemoteException {
304            enforceShortcutPermission(user);
305            verifyCallingPackage(callingPackage);
306
307            return new ParceledListSlice<>(
308                    mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
309                    componentName, flags, user.getIdentifier()));
310        }
311
312        @Override
313        public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
314                List<String> ids, UserHandle user) throws RemoteException {
315            enforceShortcutPermission(user);
316            verifyCallingPackage(callingPackage);
317
318            return new ParceledListSlice<>(
319                    mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
320                    ids, user.getIdentifier()));
321        }
322
323        @Override
324        public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
325                UserHandle user) throws RemoteException {
326            enforceShortcutPermission(user);
327            verifyCallingPackage(callingPackage);
328
329            mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
330                    ids, user.getIdentifier());
331        }
332
333        @Override
334        public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
335                UserHandle user) {
336            enforceShortcutPermission(user);
337            verifyCallingPackage(callingPackage);
338
339            return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
340                    user.getIdentifier());
341        }
342
343        @Override
344        public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
345                UserHandle user) {
346            enforceShortcutPermission(user);
347            verifyCallingPackage(callingPackage);
348
349            return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
350                    user.getIdentifier());
351        }
352
353        @Override
354        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
355                Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
356                throws RemoteException {
357            enforceShortcutPermission(user);
358            verifyCallingPackage(callingPackage);
359
360            final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
361                    packageName, shortcutId, user.getIdentifier());
362            if (intent == null) {
363                return false;
364            }
365            // Note the target activity doesn't have to be exported.
366
367            intent.setSourceBounds(sourceBounds);
368            prepareIntentForLaunch(intent, sourceBounds);
369
370            final long ident = Binder.clearCallingIdentity();
371            try {
372                mContext.startActivityAsUser(intent, startActivityOptions, user);
373            } finally {
374                Binder.restoreCallingIdentity(ident);
375            }
376            return true;
377        }
378
379        @Override
380        public boolean isActivityEnabled(ComponentName component, UserHandle user)
381                throws RemoteException {
382            ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
383            if (!isUserEnabled(user)) {
384                return false;
385            }
386
387            long ident = Binder.clearCallingIdentity();
388            try {
389                IPackageManager pm = AppGlobals.getPackageManager();
390                ActivityInfo info = pm.getActivityInfo(component,
391                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
392                return info != null;
393            } finally {
394                Binder.restoreCallingIdentity(ident);
395            }
396        }
397
398        @Override
399        public void startActivityAsUser(ComponentName component, Rect sourceBounds,
400                Bundle opts, UserHandle user) throws RemoteException {
401            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
402            if (!isUserEnabled(user)) {
403                throw new IllegalStateException("Cannot start activity for disabled profile "  + user);
404            }
405
406            Intent launchIntent = new Intent(Intent.ACTION_MAIN);
407            launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
408            prepareIntentForLaunch(launchIntent, sourceBounds);
409            launchIntent.setPackage(component.getPackageName());
410
411            long ident = Binder.clearCallingIdentity();
412            try {
413                IPackageManager pm = AppGlobals.getPackageManager();
414                ActivityInfo info = pm.getActivityInfo(component,
415                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
416                if (!info.exported) {
417                    throw new SecurityException("Cannot launch non-exported components "
418                            + component);
419                }
420
421                // Check that the component actually has Intent.CATEGORY_LAUCNCHER
422                // as calling startActivityAsUser ignores the category and just
423                // resolves based on the component if present.
424                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
425                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
426                final int size = apps.size();
427                for (int i = 0; i < size; ++i) {
428                    ActivityInfo activityInfo = apps.get(i).activityInfo;
429                    if (activityInfo.packageName.equals(component.getPackageName()) &&
430                            activityInfo.name.equals(component.getClassName())) {
431                        // Found an activity with category launcher that matches
432                        // this component so ok to launch.
433                        launchIntent.setComponent(component);
434                        mContext.startActivityAsUser(launchIntent, opts, user);
435                        return;
436                    }
437                }
438                throw new SecurityException("Attempt to launch activity without "
439                        + " category Intent.CATEGORY_LAUNCHER " + component);
440            } finally {
441                Binder.restoreCallingIdentity(ident);
442            }
443        }
444
445        private void prepareIntentForLaunch(@NonNull Intent launchIntent,
446                @Nullable Rect sourceBounds) {
447            launchIntent.setSourceBounds(sourceBounds);
448            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
449                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
450        }
451
452        @Override
453        public void showAppDetailsAsUser(ComponentName component, Rect sourceBounds,
454                Bundle opts, UserHandle user) throws RemoteException {
455            ensureInUserProfiles(user, "Cannot show app details for unrelated profile " + user);
456            if (!isUserEnabled(user)) {
457                throw new IllegalStateException("Cannot show app details for disabled profile "
458                        + user);
459            }
460
461            long ident = Binder.clearCallingIdentity();
462            try {
463                String packageName = component.getPackageName();
464                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
465                        Uri.fromParts("package", packageName, null));
466                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
467                intent.setSourceBounds(sourceBounds);
468                mContext.startActivityAsUser(intent, opts, user);
469            } finally {
470                Binder.restoreCallingIdentity(ident);
471            }
472        }
473
474
475        private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
476
477            /** Checks if user is a profile of or same as listeningUser.
478              * and the user is enabled. */
479            private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
480                    String debugMsg) {
481                if (user.getIdentifier() == listeningUser.getIdentifier()) {
482                    if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
483                    return true;
484                }
485                long ident = Binder.clearCallingIdentity();
486                try {
487                    UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
488                    UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
489                    if (userInfo == null || listeningUserInfo == null
490                            || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
491                            || userInfo.profileGroupId != listeningUserInfo.profileGroupId
492                            || !userInfo.isEnabled()) {
493                        if (DEBUG) {
494                            Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
495                                    + debugMsg);
496                        }
497                        return false;
498                    } else {
499                        if (DEBUG) {
500                            Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
501                                    + debugMsg);
502                        }
503                        return true;
504                    }
505                } finally {
506                    Binder.restoreCallingIdentity(ident);
507                }
508            }
509
510            // TODO Simplify with lambdas.
511
512            @Override
513            public void onPackageAdded(String packageName, int uid) {
514                UserHandle user = new UserHandle(getChangingUserId());
515                final int n = mListeners.beginBroadcast();
516                for (int i = 0; i < n; i++) {
517                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
518                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
519                    if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
520                    try {
521                        listener.onPackageAdded(user, packageName);
522                    } catch (RemoteException re) {
523                        Slog.d(TAG, "Callback failed ", re);
524                    }
525                }
526                mListeners.finishBroadcast();
527
528                super.onPackageAdded(packageName, uid);
529            }
530
531            @Override
532            public void onPackageRemoved(String packageName, int uid) {
533                UserHandle user = new UserHandle(getChangingUserId());
534                final int n = mListeners.beginBroadcast();
535                for (int i = 0; i < n; i++) {
536                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
537                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
538                    if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
539                    try {
540                        listener.onPackageRemoved(user, packageName);
541                    } catch (RemoteException re) {
542                        Slog.d(TAG, "Callback failed ", re);
543                    }
544                }
545                mListeners.finishBroadcast();
546
547                super.onPackageRemoved(packageName, uid);
548            }
549
550            @Override
551            public void onPackageModified(String packageName) {
552                UserHandle user = new UserHandle(getChangingUserId());
553                final int n = mListeners.beginBroadcast();
554                for (int i = 0; i < n; i++) {
555                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
556                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
557                    if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
558                    try {
559                        listener.onPackageChanged(user, packageName);
560                    } catch (RemoteException re) {
561                        Slog.d(TAG, "Callback failed ", re);
562                    }
563                }
564                mListeners.finishBroadcast();
565
566                super.onPackageModified(packageName);
567            }
568
569            @Override
570            public void onPackagesAvailable(String[] packages) {
571                UserHandle user = new UserHandle(getChangingUserId());
572                final int n = mListeners.beginBroadcast();
573                for (int i = 0; i < n; i++) {
574                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
575                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
576                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
577                    try {
578                        listener.onPackagesAvailable(user, packages, isReplacing());
579                    } catch (RemoteException re) {
580                        Slog.d(TAG, "Callback failed ", re);
581                    }
582                }
583                mListeners.finishBroadcast();
584
585                super.onPackagesAvailable(packages);
586            }
587
588            @Override
589            public void onPackagesUnavailable(String[] packages) {
590                UserHandle user = new UserHandle(getChangingUserId());
591                final int n = mListeners.beginBroadcast();
592                for (int i = 0; i < n; i++) {
593                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
594                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
595                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
596                    try {
597                        listener.onPackagesUnavailable(user, packages, isReplacing());
598                    } catch (RemoteException re) {
599                        Slog.d(TAG, "Callback failed ", re);
600                    }
601                }
602                mListeners.finishBroadcast();
603
604                super.onPackagesUnavailable(packages);
605            }
606
607            @Override
608            public void onPackagesSuspended(String[] packages) {
609                UserHandle user = new UserHandle(getChangingUserId());
610                final int n = mListeners.beginBroadcast();
611                for (int i = 0; i < n; i++) {
612                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
613                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
614                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesSuspended")) continue;
615                    try {
616                        listener.onPackagesSuspended(user, packages);
617                    } catch (RemoteException re) {
618                        Slog.d(TAG, "Callback failed ", re);
619                    }
620                }
621                mListeners.finishBroadcast();
622
623                super.onPackagesSuspended(packages);
624            }
625
626            @Override
627            public void onPackagesUnsuspended(String[] packages) {
628                UserHandle user = new UserHandle(getChangingUserId());
629                final int n = mListeners.beginBroadcast();
630                for (int i = 0; i < n; i++) {
631                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
632                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
633                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnsuspended")) continue;
634                    try {
635                        listener.onPackagesUnsuspended(user, packages);
636                    } catch (RemoteException re) {
637                        Slog.d(TAG, "Callback failed ", re);
638                    }
639                }
640                mListeners.finishBroadcast();
641
642                super.onPackagesUnsuspended(packages);
643            }
644
645            @Override
646            public void onShortcutChanged(@NonNull String packageName,
647                    @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId) {
648                final UserHandle user = UserHandle.of(userId);
649
650                final int n = mListeners.beginBroadcast();
651                for (int i = 0; i < n; i++) {
652                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
653                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
654                    if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue;
655                    try {
656                        listener.onShortcutChanged(user, packageName,
657                                new ParceledListSlice<>(shortcuts));
658                    } catch (RemoteException re) {
659                        Slog.d(TAG, "Callback failed ", re);
660                    }
661                }
662                mListeners.finishBroadcast();
663            }
664        }
665
666        class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
667            @Override
668            public void onCallbackDied(T callback, Object cookie) {
669                checkCallbackCount();
670            }
671        }
672    }
673}
674