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