1 /*
2  * Copyright (C) 2017 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 package android.content.pm;
17 
18 import android.annotation.NonNull;
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.graphics.drawable.Drawable;
25 import android.os.RemoteException;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 
29 import com.android.internal.R;
30 import com.android.internal.util.UserIcons;
31 
32 import java.util.List;
33 
34 /**
35  * Class for handling cross profile operations. Apps can use this class to interact with its
36  * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
37  * use this class to start its main activity in managed profile.
38  */
39 public class CrossProfileApps {
40     private final Context mContext;
41     private final ICrossProfileApps mService;
42     private final UserManager mUserManager;
43     private final Resources mResources;
44 
45     /** @hide */
CrossProfileApps(Context context, ICrossProfileApps service)46     public CrossProfileApps(Context context, ICrossProfileApps service) {
47         mContext = context;
48         mService = service;
49         mUserManager = context.getSystemService(UserManager.class);
50         mResources = context.getResources();
51     }
52 
53     /**
54      * Starts the specified main activity of the caller package in the specified profile.
55      *
56      * @param component The ComponentName of the activity to launch, it must be exported and has
57      *        action {@link android.content.Intent#ACTION_MAIN}, category
58      *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
59      *        be thrown.
60      * @param targetUser The UserHandle of the profile, must be one of the users returned by
61      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
62      *        be thrown.
63      */
startMainActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)64     public void startMainActivity(@NonNull ComponentName component,
65             @NonNull UserHandle targetUser) {
66         try {
67             mService.startActivityAsUser(
68                     mContext.getIApplicationThread(),
69                     mContext.getPackageName(),
70                     component,
71                     targetUser.getIdentifier(),
72                     true);
73         } catch (RemoteException ex) {
74             throw ex.rethrowFromSystemServer();
75         }
76     }
77 
78     /**
79      * Starts the specified activity of the caller package in the specified profile. Unlike
80      * {@link #startMainActivity}, this can start any activity of the caller package, not just
81      * the main activity.
82      * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
83      * permission and both the caller and target user profiles must be in the same profile group.
84      *
85      * @param component The ComponentName of the activity to launch. It must be exported.
86      * @param targetUser The UserHandle of the profile, must be one of the users returned by
87      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
88      *        be thrown.
89      * @hide
90      */
91     @SystemApi
92     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
startActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)93     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
94         try {
95             mService.startActivityAsUser(mContext.getIApplicationThread(),
96                     mContext.getPackageName(), component, targetUser.getIdentifier(), false);
97         } catch (RemoteException ex) {
98             throw ex.rethrowFromSystemServer();
99         }
100     }
101 
102     /**
103      * Return a list of user profiles that that the caller can use when calling other APIs in this
104      * class.
105      * <p>
106      * A user profile would be considered as a valid target user profile, provided that:
107      * <ul>
108      * <li>It gets caller app installed</li>
109      * <li>It is not equal to the calling user</li>
110      * <li>It is in the same profile group of calling user profile</li>
111      * <li>It is enabled</li>
112      * </ul>
113      *
114      * @see UserManager#getUserProfiles()
115      */
getTargetUserProfiles()116     public @NonNull List<UserHandle> getTargetUserProfiles() {
117         try {
118             return mService.getTargetUserProfiles(mContext.getPackageName());
119         } catch (RemoteException ex) {
120             throw ex.rethrowFromSystemServer();
121         }
122     }
123 
124     /**
125      * Return a label that calling app can show to user for the semantic of profile switching --
126      * launching its own activity in specified user profile. For example, it may return
127      * "Switch to work" if the given user handle is the managed profile one.
128      *
129      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
130      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
131      *        be thrown.
132      * @return a label that calling app can show user for the semantic of launching its own
133      *         activity in the specified user profile.
134      *
135      * @see #startMainActivity(ComponentName, UserHandle)
136      */
getProfileSwitchingLabel(@onNull UserHandle userHandle)137     public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
138         verifyCanAccessUser(userHandle);
139 
140         final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
141                 ? R.string.managed_profile_label
142                 : R.string.user_owner_label;
143         return mResources.getString(stringRes);
144     }
145 
146     /**
147      * Return a drawable that calling app can show to user for the semantic of profile switching --
148      * launching its own activity in specified user profile. For example, it may return a briefcase
149      * icon if the given user handle is the managed profile one.
150      *
151      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
152      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
153      *        be thrown.
154      * @return an icon that calling app can show user for the semantic of launching its own
155      *         activity in specified user profile.
156      *
157      * @see #startMainActivity(ComponentName, UserHandle)
158      */
getProfileSwitchingIconDrawable(@onNull UserHandle userHandle)159     public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
160         verifyCanAccessUser(userHandle);
161 
162         final boolean isManagedProfile =
163                 mUserManager.isManagedProfile(userHandle.getIdentifier());
164         if (isManagedProfile) {
165             return mResources.getDrawable(R.drawable.ic_corp_badge, null);
166         } else {
167             return UserIcons.getDefaultUserIcon(
168                     mResources, UserHandle.USER_SYSTEM, true /* light */);
169         }
170     }
171 
verifyCanAccessUser(UserHandle userHandle)172     private void verifyCanAccessUser(UserHandle userHandle) {
173         if (!getTargetUserProfiles().contains(userHandle)) {
174             throw new SecurityException("Not allowed to access " + userHandle);
175         }
176     }
177 }
178