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