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