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