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