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