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