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