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