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