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