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