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.content.ComponentName;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 import android.os.Bundle;
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);
72         } catch (RemoteException ex) {
73             throw ex.rethrowFromSystemServer();
74         }
75     }
76 
77     /**
78      * Return a list of user profiles that that the caller can use when calling other APIs in this
79      * class.
80      * <p>
81      * A user profile would be considered as a valid target user profile, provided that:
82      * <ul>
83      * <li>It gets caller app installed</li>
84      * <li>It is not equal to the calling user</li>
85      * <li>It is in the same profile group of calling user profile</li>
86      * <li>It is enabled</li>
87      * </ul>
88      *
89      * @see UserManager#getUserProfiles()
90      */
getTargetUserProfiles()91     public @NonNull List<UserHandle> getTargetUserProfiles() {
92         try {
93             return mService.getTargetUserProfiles(mContext.getPackageName());
94         } catch (RemoteException ex) {
95             throw ex.rethrowFromSystemServer();
96         }
97     }
98 
99     /**
100      * Return a label that calling app can show to user for the semantic of profile switching --
101      * launching its own activity in specified user profile. For example, it may return
102      * "Switch to work" if the given user handle is the managed profile one.
103      *
104      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
105      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
106      *        be thrown.
107      * @return a label that calling app can show user for the semantic of launching its own
108      *         activity in the specified user profile.
109      *
110      * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
111      */
getProfileSwitchingLabel(@onNull UserHandle userHandle)112     public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
113         verifyCanAccessUser(userHandle);
114 
115         final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
116                 ? R.string.managed_profile_label
117                 : R.string.user_owner_label;
118         return mResources.getString(stringRes);
119     }
120 
121     /**
122      * Return a drawable that calling app can show to user for the semantic of profile switching --
123      * launching its own activity in specified user profile. For example, it may return a briefcase
124      * icon if the given user handle is the managed profile one.
125      *
126      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
127      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
128      *        be thrown.
129      * @return an icon that calling app can show user for the semantic of launching its own
130      *         activity in specified user profile.
131      *
132      * @see #startMainActivity(ComponentName, UserHandle)
133      */
getProfileSwitchingIconDrawable(@onNull UserHandle userHandle)134     public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
135         verifyCanAccessUser(userHandle);
136 
137         final boolean isManagedProfile =
138                 mUserManager.isManagedProfile(userHandle.getIdentifier());
139         if (isManagedProfile) {
140             return mResources.getDrawable(R.drawable.ic_corp_badge, null);
141         } else {
142             return UserIcons.getDefaultUserIcon(
143                     mResources, UserHandle.USER_SYSTEM, true /* light */);
144         }
145     }
146 
verifyCanAccessUser(UserHandle userHandle)147     private void verifyCanAccessUser(UserHandle userHandle) {
148         if (!getTargetUserProfiles().contains(userHandle)) {
149             throw new SecurityException("Not allowed to access " + userHandle);
150         }
151     }
152 }
153