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