LauncherAppsService.java revision 3cc7cd1c18f5d5b2df0f8aebb22fdb3feb23ecd0
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                    Slog.w(TAG, message + " by " + callingPackage + " for another profile "
246                            + targetUserId + " from " + callingUserId);
247                    return false;
248                }
249
250                UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
251                if (targetUserInfo == null
252                        || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
253                        || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
254                    throw new SecurityException(message + " for unrelated profile " + targetUserId);
255                }
256            } finally {
257                injectRestoreCallingIdentity(ident);
258            }
259            return true;
260        }
261
262        @VisibleForTesting // We override it in unit tests
263        void verifyCallingPackage(String callingPackage) {
264            int packageUid = -1;
265            try {
266                packageUid = mPm.getPackageUidAsUser(callingPackage,
267                        PackageManager.MATCH_DIRECT_BOOT_AWARE
268                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
269                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
270                        UserHandle.getUserId(getCallingUid()));
271            } catch (NameNotFoundException e) {
272                Log.e(TAG, "Package not found: " + callingPackage);
273            }
274            if (packageUid != Binder.getCallingUid()) {
275                throw new SecurityException("Calling package name mismatch");
276            }
277        }
278
279        /**
280         * Checks if the user is enabled.
281         */
282        private boolean isUserEnabled(UserHandle user) {
283            return isUserEnabled(user.getIdentifier());
284        }
285
286        private boolean isUserEnabled(int userId) {
287            long ident = injectClearCallingIdentity();
288            try {
289                UserInfo targetUserInfo = mUm.getUserInfo(userId);
290                return targetUserInfo != null && targetUserInfo.isEnabled();
291            } finally {
292                injectRestoreCallingIdentity(ident);
293            }
294        }
295
296        @Override
297        public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
298                String packageName, UserHandle user)
299                throws RemoteException {
300            return queryActivitiesForUser(callingPackage,
301                    new Intent(Intent.ACTION_MAIN)
302                            .addCategory(Intent.CATEGORY_LAUNCHER)
303                            .setPackage(packageName),
304                    user);
305        }
306
307        @Override
308        public ActivityInfo resolveActivity(
309                String callingPackage, ComponentName component, UserHandle user)
310                throws RemoteException {
311            if (!canAccessProfile(callingPackage, user, "Cannot resolve activity")) {
312                return null;
313            }
314            if (!isUserEnabled(user)) {
315                return null;
316            }
317
318            long ident = Binder.clearCallingIdentity();
319            try {
320                IPackageManager pm = AppGlobals.getPackageManager();
321                return pm.getActivityInfo(component,
322                        PackageManager.MATCH_DIRECT_BOOT_AWARE
323                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
324                        user.getIdentifier());
325            } finally {
326                Binder.restoreCallingIdentity(ident);
327            }
328        }
329
330        @Override
331        public ParceledListSlice getShortcutConfigActivities(
332                String callingPackage, String packageName, UserHandle user)
333                throws RemoteException {
334            return queryActivitiesForUser(callingPackage,
335                    new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
336        }
337
338        private ParceledListSlice<ResolveInfo> queryActivitiesForUser(String callingPackage,
339                Intent intent, UserHandle user) {
340            if (!canAccessProfile(callingPackage, user, "Cannot retrieve activities")) {
341                return null;
342            }
343            if (!isUserEnabled(user)) {
344                return null;
345            }
346
347            long ident = injectClearCallingIdentity();
348            try {
349                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(intent,
350                        PackageManager.MATCH_DIRECT_BOOT_AWARE
351                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
352                        user.getIdentifier());
353                return new ParceledListSlice<>(apps);
354            } finally {
355                injectRestoreCallingIdentity(ident);
356            }
357        }
358
359        @Override
360        public IntentSender getShortcutConfigActivityIntent(String callingPackage,
361                ComponentName component, UserHandle user) throws RemoteException {
362            ensureShortcutPermission(callingPackage);
363            if (!canAccessProfile(callingPackage, user, "Cannot check package")) {
364                return null;
365            }
366            Preconditions.checkNotNull(component);
367            Preconditions.checkArgument(isUserEnabled(user), "User not enabled");
368
369            // All right, create the sender.
370            Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
371            final long identity = Binder.clearCallingIdentity();
372            try {
373                final PendingIntent pi = PendingIntent.getActivityAsUser(
374                        mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
375                                | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
376                        null, user);
377                return pi == null ? null : pi.getIntentSender();
378            } finally {
379                Binder.restoreCallingIdentity(identity);
380            }
381        }
382
383        @Override
384        public boolean isPackageEnabled(String callingPackage, String packageName, UserHandle user)
385                throws RemoteException {
386            if (!canAccessProfile(callingPackage, user, "Cannot check package")) {
387                return false;
388            }
389            if (!isUserEnabled(user)) {
390                return false;
391            }
392
393            long ident = Binder.clearCallingIdentity();
394            try {
395                IPackageManager pm = AppGlobals.getPackageManager();
396                PackageInfo info = pm.getPackageInfo(packageName,
397                        PackageManager.MATCH_DIRECT_BOOT_AWARE
398                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
399                        user.getIdentifier());
400                return info != null && info.applicationInfo.enabled;
401            } finally {
402                Binder.restoreCallingIdentity(ident);
403            }
404        }
405
406        @Override
407        public ApplicationInfo getApplicationInfo(
408                String callingPackage, String packageName, int flags, UserHandle user)
409                throws RemoteException {
410            if (!canAccessProfile(callingPackage, user, "Cannot check package")) {
411                return null;
412            }
413            if (!isUserEnabled(user)) {
414                return null;
415            }
416
417            long ident = Binder.clearCallingIdentity();
418            try {
419                IPackageManager pm = AppGlobals.getPackageManager();
420                ApplicationInfo info = pm.getApplicationInfo(packageName, flags,
421                        user.getIdentifier());
422                return info;
423            } finally {
424                Binder.restoreCallingIdentity(ident);
425            }
426        }
427
428        private void ensureShortcutPermission(@NonNull String callingPackage) {
429            verifyCallingPackage(callingPackage);
430            if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
431                    callingPackage)) {
432                throw new SecurityException("Caller can't access shortcut information");
433            }
434        }
435
436        @Override
437        public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
438                String packageName, List shortcutIds, ComponentName componentName, Intent intent,
439                int flags, UserHandle targetUser) {
440            ensureShortcutPermission(callingPackage);
441            if (!canAccessProfile(callingPackage, targetUser, "Cannot get shortcuts")
442                    || !isUserEnabled(targetUser)) {
443                return new ParceledListSlice<>(Collections.EMPTY_LIST);
444            }
445            if (shortcutIds != null && packageName == null) {
446                throw new IllegalArgumentException(
447                        "To query by shortcut ID, package name must also be set");
448            }
449
450            if ((flags & ShortcutQuery.FLAG_MATCH_CHOOSER) == 0
451                    && intent != null) {
452                throw new IllegalArgumentException("Supplied an intent in the query, but did "
453                        + "not request chooser targets");
454            }
455
456            // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
457            return new ParceledListSlice<>((List<ShortcutInfo>)
458                    mShortcutServiceInternal.getShortcuts(getCallingUserId(),
459                            callingPackage, changedSince, packageName, shortcutIds,
460                            componentName, intent, flags, targetUser.getIdentifier()));
461        }
462
463        @Override
464        public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
465                UserHandle targetUser) {
466            ensureShortcutPermission(callingPackage);
467            if (!canAccessProfile(callingPackage, targetUser, "Cannot pin shortcuts")) {
468                return;
469            }
470            if (!isUserEnabled(targetUser)) {
471                throw new IllegalStateException("Cannot pin shortcuts for disabled profile "
472                        + targetUser);
473            }
474
475            mShortcutServiceInternal.pinShortcuts(getCallingUserId(),
476                    callingPackage, packageName, ids, targetUser.getIdentifier());
477        }
478
479        @Override
480        public int getShortcutIconResId(String callingPackage, String packageName, String id,
481                int targetUserId) {
482            ensureShortcutPermission(callingPackage);
483            if (!canAccessProfile(callingPackage, targetUserId, "Cannot access shortcuts")) {
484                return 0;
485            }
486            if (!isUserEnabled(targetUserId)) {
487                return 0;
488            }
489
490            return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
491                    callingPackage, packageName, id, targetUserId);
492        }
493
494        @Override
495        public ParcelFileDescriptor getShortcutIconFd(String callingPackage,
496                String packageName, String id, int targetUserId) {
497            ensureShortcutPermission(callingPackage);
498            if (!canAccessProfile(callingPackage, targetUserId, "Cannot access shortcuts")) {
499                return null;
500            }
501            if (!isUserEnabled(targetUserId)) {
502                return null;
503            }
504
505            return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
506                    callingPackage, packageName, id, targetUserId);
507        }
508
509        @Override
510        public boolean hasShortcutHostPermission(String callingPackage) {
511            verifyCallingPackage(callingPackage);
512            return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
513                    callingPackage);
514        }
515
516        @Override
517        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
518                Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
519            verifyCallingPackage(callingPackage);
520            if (!canAccessProfile(callingPackage, targetUserId, "Cannot start activity")) {
521                return false;
522            }
523            if (!isUserEnabled(targetUserId)) {
524                throw new IllegalStateException("Cannot start a shortcut for disabled profile "
525                        + targetUserId);
526            }
527
528            // Even without the permission, pinned shortcuts are always launchable.
529            if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
530                    callingPackage, packageName, shortcutId, targetUserId)) {
531                ensureShortcutPermission(callingPackage);
532            }
533
534            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
535                    getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId);
536            if (intents == null || intents.length == 0) {
537                return false;
538            }
539            // Note the target activity doesn't have to be exported.
540
541            intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
542            intents[0].setSourceBounds(sourceBounds);
543
544            return startShortcutIntentsAsPublisher(
545                    intents, packageName, startActivityOptions, targetUserId);
546        }
547
548        private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
549                @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
550            final int code;
551            final long ident = injectClearCallingIdentity();
552            try {
553                code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
554                        userId, intents, startActivityOptions);
555                if (code >= ActivityManager.START_SUCCESS) {
556                    return true; // Success
557                } else {
558                    Log.e(TAG, "Couldn't start activity, code=" + code);
559                }
560                return code >= ActivityManager.START_SUCCESS;
561            } catch (SecurityException e) {
562                if (DEBUG) {
563                    Slog.d(TAG, "SecurityException while launching intent", e);
564                }
565                return false;
566            } finally {
567                injectRestoreCallingIdentity(ident);
568            }
569        }
570
571        @Override
572        public boolean isActivityEnabled(
573                String callingPackage, ComponentName component, UserHandle user)
574                throws RemoteException {
575            if (!canAccessProfile(callingPackage , user, "Cannot check component")) {
576                return false;
577            }
578            if (!isUserEnabled(user)) {
579                return false;
580            }
581
582            long ident = Binder.clearCallingIdentity();
583            try {
584                IPackageManager pm = AppGlobals.getPackageManager();
585                ActivityInfo info = pm.getActivityInfo(component,
586                        PackageManager.MATCH_DIRECT_BOOT_AWARE
587                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
588                        user.getIdentifier());
589                return info != null;
590            } finally {
591                Binder.restoreCallingIdentity(ident);
592            }
593        }
594
595        @Override
596        public void startActivityAsUser(String callingPackage,
597                ComponentName component, Rect sourceBounds,
598                Bundle opts, UserHandle user) throws RemoteException {
599            if (!canAccessProfile(callingPackage, user, "Cannot start activity")) {
600                return;
601            }
602            if (!isUserEnabled(user)) {
603                throw new IllegalStateException("Cannot start activity for disabled profile "  + user);
604            }
605
606            Intent launchIntent = new Intent(Intent.ACTION_MAIN);
607            launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
608            launchIntent.setSourceBounds(sourceBounds);
609            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
610                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
611            launchIntent.setPackage(component.getPackageName());
612
613            long ident = Binder.clearCallingIdentity();
614            try {
615                IPackageManager pm = AppGlobals.getPackageManager();
616                ActivityInfo info = pm.getActivityInfo(component,
617                        PackageManager.MATCH_DIRECT_BOOT_AWARE
618                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
619                        user.getIdentifier());
620                if (!info.exported) {
621                    throw new SecurityException("Cannot launch non-exported components "
622                            + component);
623                }
624
625                // Check that the component actually has Intent.CATEGORY_LAUCNCHER
626                // as calling startActivityAsUser ignores the category and just
627                // resolves based on the component if present.
628                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
629                        PackageManager.MATCH_DIRECT_BOOT_AWARE
630                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
631                        user.getIdentifier());
632                final int size = apps.size();
633                for (int i = 0; i < size; ++i) {
634                    ActivityInfo activityInfo = apps.get(i).activityInfo;
635                    if (activityInfo.packageName.equals(component.getPackageName()) &&
636                            activityInfo.name.equals(component.getClassName())) {
637                        // Found an activity with category launcher that matches
638                        // this component so ok to launch.
639                        launchIntent.setComponent(component);
640                        mContext.startActivityAsUser(launchIntent, opts, user);
641                        return;
642                    }
643                }
644                throw new SecurityException("Attempt to launch activity without "
645                        + " category Intent.CATEGORY_LAUNCHER " + component);
646            } finally {
647                Binder.restoreCallingIdentity(ident);
648            }
649        }
650
651        @Override
652        public void showAppDetailsAsUser(String callingPackage, ComponentName component,
653                Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
654            if (!canAccessProfile(callingPackage, user, "Cannot show app details")) {
655                return;
656            }
657            if (!isUserEnabled(user)) {
658                throw new IllegalStateException("Cannot show app details for disabled profile "
659                        + user);
660            }
661
662            long ident = Binder.clearCallingIdentity();
663            try {
664                String packageName = component.getPackageName();
665                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
666                        Uri.fromParts("package", packageName, null));
667                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
668                intent.setSourceBounds(sourceBounds);
669                mContext.startActivityAsUser(intent, opts, user);
670            } finally {
671                Binder.restoreCallingIdentity(ident);
672            }
673        }
674
675        /** Checks if user is a profile of or same as listeningUser.
676         * and the user is enabled. */
677        private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
678                String debugMsg) {
679            if (user.getIdentifier() == listeningUser.getIdentifier()) {
680                if (DEBUG) Log.d(TAG, "Delivering msg to same user: " + debugMsg);
681                return true;
682            }
683            if (mUm.isManagedProfile(listeningUser.getIdentifier())) {
684                if (DEBUG) Log.d(TAG, "Managed profile can't see other profiles: " + debugMsg);
685                return false;
686            }
687            long ident = injectClearCallingIdentity();
688            try {
689                UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
690                UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
691                if (userInfo == null || listeningUserInfo == null
692                        || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
693                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId
694                        || !userInfo.isEnabled()) {
695                    if (DEBUG) {
696                        Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
697                                + debugMsg);
698                    }
699                    return false;
700                } else {
701                    if (DEBUG) {
702                        Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
703                                + debugMsg);
704                    }
705                    return true;
706                }
707            } finally {
708                injectRestoreCallingIdentity(ident);
709            }
710        }
711
712        @VisibleForTesting
713        void postToPackageMonitorHandler(Runnable r) {
714            mCallbackHandler.post(r);
715        }
716
717        private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
718
719            // TODO Simplify with lambdas.
720
721            @Override
722            public void onPackageAdded(String packageName, int uid) {
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(user, cookie.user, "onPackageAdded")) continue;
730                        try {
731                            listener.onPackageAdded(user, packageName);
732                        } catch (RemoteException re) {
733                            Slog.d(TAG, "Callback failed ", re);
734                        }
735                    }
736                } finally {
737                    mListeners.finishBroadcast();
738                }
739
740                super.onPackageAdded(packageName, uid);
741            }
742
743            @Override
744            public void onPackageRemoved(String packageName, int uid) {
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(user, cookie.user, "onPackageRemoved")) continue;
752                        try {
753                            listener.onPackageRemoved(user, packageName);
754                        } catch (RemoteException re) {
755                            Slog.d(TAG, "Callback failed ", re);
756                        }
757                    }
758                } finally {
759                    mListeners.finishBroadcast();
760                }
761
762                super.onPackageRemoved(packageName, uid);
763            }
764
765            @Override
766            public void onPackageModified(String packageName) {
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(user, cookie.user, "onPackageModified")) continue;
774                        try {
775                            listener.onPackageChanged(user, packageName);
776                        } catch (RemoteException re) {
777                            Slog.d(TAG, "Callback failed ", re);
778                        }
779                    }
780                } finally {
781                    mListeners.finishBroadcast();
782                }
783
784                super.onPackageModified(packageName);
785            }
786
787            @Override
788            public void onPackagesAvailable(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(user, cookie.user, "onPackagesAvailable")) continue;
796                        try {
797                            listener.onPackagesAvailable(user, packages, isReplacing());
798                        } catch (RemoteException re) {
799                            Slog.d(TAG, "Callback failed ", re);
800                        }
801                    }
802                } finally {
803                    mListeners.finishBroadcast();
804                }
805
806                super.onPackagesAvailable(packages);
807            }
808
809            @Override
810            public void onPackagesUnavailable(String[] packages) {
811                UserHandle user = new UserHandle(getChangingUserId());
812                final int n = mListeners.beginBroadcast();
813                try {
814                    for (int i = 0; i < n; i++) {
815                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
816                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
817                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnavailable")) continue;
818                        try {
819                            listener.onPackagesUnavailable(user, packages, isReplacing());
820                        } catch (RemoteException re) {
821                            Slog.d(TAG, "Callback failed ", re);
822                        }
823                    }
824                } finally {
825                    mListeners.finishBroadcast();
826                }
827
828                super.onPackagesUnavailable(packages);
829            }
830
831            @Override
832            public void onPackagesSuspended(String[] packages) {
833                UserHandle user = new UserHandle(getChangingUserId());
834                final int n = mListeners.beginBroadcast();
835                try {
836                    for (int i = 0; i < n; i++) {
837                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
838                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
839                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesSuspended")) continue;
840                        try {
841                            listener.onPackagesSuspended(user, packages);
842                        } catch (RemoteException re) {
843                            Slog.d(TAG, "Callback failed ", re);
844                        }
845                    }
846                } finally {
847                    mListeners.finishBroadcast();
848                }
849
850                super.onPackagesSuspended(packages);
851            }
852
853            @Override
854            public void onPackagesUnsuspended(String[] packages) {
855                UserHandle user = new UserHandle(getChangingUserId());
856                final int n = mListeners.beginBroadcast();
857                try {
858                    for (int i = 0; i < n; i++) {
859                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
860                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
861                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnsuspended")) continue;
862                        try {
863                            listener.onPackagesUnsuspended(user, packages);
864                        } catch (RemoteException re) {
865                            Slog.d(TAG, "Callback failed ", re);
866                        }
867                    }
868                } finally {
869                    mListeners.finishBroadcast();
870                }
871
872                super.onPackagesUnsuspended(packages);
873            }
874
875            @Override
876            public void onShortcutChanged(@NonNull String packageName,
877                    @UserIdInt int userId) {
878                postToPackageMonitorHandler(() -> onShortcutChangedInner(packageName, userId));
879            }
880
881            private void onShortcutChangedInner(@NonNull String packageName,
882                    @UserIdInt int userId) {
883                final int n = mListeners.beginBroadcast();
884                try {
885                    final UserHandle user = UserHandle.of(userId);
886
887                    for (int i = 0; i < n; i++) {
888                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
889                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
890                        if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
891
892                        final int launcherUserId = cookie.user.getIdentifier();
893
894                        // Make sure the caller has the permission.
895                        if (!mShortcutServiceInternal.hasShortcutHostPermission(
896                                launcherUserId, cookie.packageName)) {
897                            continue;
898                        }
899                        // Each launcher has a different set of pinned shortcuts, so we need to do a
900                        // query in here.
901                        // (As of now, only one launcher has the permission at a time, so it's bit
902                        // moot, but we may change the permission model eventually.)
903                        final List<ShortcutInfo> list =
904                                mShortcutServiceInternal.getShortcuts(launcherUserId,
905                                        cookie.packageName,
906                                        /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
907                                        /* component= */ null,
908                                        /* intent= */ null,
909                                        ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
910                                        | ShortcutQuery.FLAG_GET_ALL_KINDS
911                                        , userId);
912                        try {
913                            listener.onShortcutChanged(user, packageName,
914                                    new ParceledListSlice<>(list));
915                        } catch (RemoteException re) {
916                            Slog.d(TAG, "Callback failed ", re);
917                        }
918                    }
919                } catch (RuntimeException e) {
920                    // When the user is locked we get IllegalState, so just catch all.
921                    Log.w(TAG, e.getMessage(), e);
922                } finally {
923                    mListeners.finishBroadcast();
924                }
925            }
926        }
927
928        class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
929            @Override
930            public void onCallbackDied(T callback, Object cookie) {
931                checkCallbackCount();
932            }
933        }
934    }
935}
936