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