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