Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 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 | */ |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 16 | package android.content.pm; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 17 | |
| 18 | import android.annotation.NonNull; |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 19 | import android.annotation.Nullable; |
Varun Shah | acad138 | 2018-11-28 11:56:08 -0800 | [diff] [blame] | 20 | import android.annotation.RequiresPermission; |
| 21 | import android.annotation.SystemApi; |
Alex Kershaw | 16406c3 | 2020-02-17 12:28:53 +0000 | [diff] [blame] | 22 | import android.app.Activity; |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 23 | import android.app.AppOpsManager.Mode; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 24 | import android.content.ComponentName; |
| 25 | import android.content.Context; |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 26 | import android.content.Intent; |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 27 | import android.content.res.Resources; |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 28 | import android.graphics.drawable.Drawable; |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 29 | import android.net.Uri; |
Alex Kershaw | a9f0052 | 2020-03-10 16:29:49 +0000 | [diff] [blame] | 30 | import android.os.Bundle; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 31 | import android.os.RemoteException; |
| 32 | import android.os.UserHandle; |
| 33 | import android.os.UserManager; |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 34 | import android.provider.Settings; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 35 | |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 36 | import com.android.internal.R; |
| 37 | import com.android.internal.util.UserIcons; |
| 38 | |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 39 | import java.util.Collection; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 40 | import java.util.List; |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 41 | import java.util.Set; |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 42 | import java.util.stream.Collectors; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 43 | |
| 44 | /** |
| 45 | * Class for handling cross profile operations. Apps can use this class to interact with its |
| 46 | * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can |
| 47 | * use this class to start its main activity in managed profile. |
| 48 | */ |
| 49 | public class CrossProfileApps { |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 50 | |
| 51 | /** |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 52 | * Broadcast signalling that the receiving app's permission to interact across profiles has |
| 53 | * changed. This includes the user, admin, or OEM changing their consent such that the |
| 54 | * permission for the app to interact across profiles has changed. |
| 55 | * |
| 56 | * <p>This broadcast is not sent when other circumstances result in a change to being able to |
| 57 | * interact across profiles in practice, such as the profile being turned off or removed, apps |
| 58 | * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link |
| 59 | * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact |
| 60 | * across profiles or attempting to request user consent to interact across profiles. |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 61 | * |
| 62 | * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true} |
| 63 | * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be |
| 64 | * received by dynamically-registered broadcast receivers. |
| 65 | */ |
| 66 | public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = |
| 67 | "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"; |
| 68 | |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 69 | private final Context mContext; |
| 70 | private final ICrossProfileApps mService; |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 71 | private final UserManager mUserManager; |
| 72 | private final Resources mResources; |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 73 | |
| 74 | /** @hide */ |
| 75 | public CrossProfileApps(Context context, ICrossProfileApps service) { |
| 76 | mContext = context; |
| 77 | mService = service; |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 78 | mUserManager = context.getSystemService(UserManager.class); |
| 79 | mResources = context.getResources(); |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Starts the specified main activity of the caller package in the specified profile. |
| 84 | * |
| 85 | * @param component The ComponentName of the activity to launch, it must be exported and has |
| 86 | * action {@link android.content.Intent#ACTION_MAIN}, category |
| 87 | * {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will |
| 88 | * be thrown. |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 89 | * @param targetUser The UserHandle of the profile, must be one of the users returned by |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 90 | * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will |
| 91 | * be thrown. |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 92 | */ |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 93 | public void startMainActivity(@NonNull ComponentName component, |
| 94 | @NonNull UserHandle targetUser) { |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 95 | try { |
Tony Mak | de32b83 | 2018-04-30 15:11:57 +0100 | [diff] [blame] | 96 | mService.startActivityAsUser( |
| 97 | mContext.getIApplicationThread(), |
| 98 | mContext.getPackageName(), |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame^] | 99 | mContext.getAttributionTag(), |
Tony Mak | de32b83 | 2018-04-30 15:11:57 +0100 | [diff] [blame] | 100 | component, |
Varun Shah | acad138 | 2018-11-28 11:56:08 -0800 | [diff] [blame] | 101 | targetUser.getIdentifier(), |
| 102 | true); |
| 103 | } catch (RemoteException ex) { |
| 104 | throw ex.rethrowFromSystemServer(); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /** |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 109 | * Starts the specified activity of the caller package in the specified profile. |
| 110 | * |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 111 | * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}, |
| 112 | * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code |
| 113 | * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and |
| 114 | * target user profiles must be in the same profile group. The target user must be a valid user |
| 115 | * returned from {@link #getTargetUserProfiles()}. |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 116 | * |
| 117 | * @param intent The intent to launch. A component in the caller package must be specified. |
| 118 | * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by |
| 119 | * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a |
| 120 | * {@link SecurityException} will be thrown. |
Alex Kershaw | 16406c3 | 2020-02-17 12:28:53 +0000 | [diff] [blame] | 121 | * @param callingActivity The activity to start the new activity from for the purposes of |
| 122 | * deciding which task the new activity should belong to. If {@code null}, the activity |
| 123 | * will always be started in a new task. |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 124 | */ |
| 125 | @RequiresPermission(anyOf = { |
| 126 | android.Manifest.permission.INTERACT_ACROSS_PROFILES, |
| 127 | android.Manifest.permission.INTERACT_ACROSS_USERS}) |
Alex Kershaw | 16406c3 | 2020-02-17 12:28:53 +0000 | [diff] [blame] | 128 | public void startActivity( |
| 129 | @NonNull Intent intent, |
| 130 | @NonNull UserHandle targetUser, |
| 131 | @Nullable Activity callingActivity) { |
Alex Kershaw | a9f0052 | 2020-03-10 16:29:49 +0000 | [diff] [blame] | 132 | startActivity(intent, targetUser, callingActivity, /* options= */ null); |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Starts the specified activity of the caller package in the specified profile. |
| 137 | * |
| 138 | * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}, |
| 139 | * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code |
| 140 | * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and |
| 141 | * target user profiles must be in the same profile group. The target user must be a valid user |
| 142 | * returned from {@link #getTargetUserProfiles()}. |
| 143 | * |
| 144 | * @param intent The intent to launch. A component in the caller package must be specified. |
| 145 | * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by |
| 146 | * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a |
| 147 | * {@link SecurityException} will be thrown. |
| 148 | * @param callingActivity The activity to start the new activity from for the purposes of |
| 149 | * deciding which task the new activity should belong to. If {@code null}, the activity |
| 150 | * will always be started in a new task. |
| 151 | * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}. |
| 152 | */ |
| 153 | @RequiresPermission(anyOf = { |
| 154 | android.Manifest.permission.INTERACT_ACROSS_PROFILES, |
| 155 | android.Manifest.permission.INTERACT_ACROSS_USERS}) |
| 156 | public void startActivity( |
| 157 | @NonNull Intent intent, |
| 158 | @NonNull UserHandle targetUser, |
| 159 | @Nullable Activity callingActivity, |
| 160 | @Nullable Bundle options) { |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 161 | try { |
| 162 | mService.startActivityAsUserByIntent( |
| 163 | mContext.getIApplicationThread(), |
| 164 | mContext.getPackageName(), |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame^] | 165 | mContext.getAttributionTag(), |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 166 | intent, |
Alex Kershaw | 16406c3 | 2020-02-17 12:28:53 +0000 | [diff] [blame] | 167 | targetUser.getIdentifier(), |
Alex Kershaw | a9f0052 | 2020-03-10 16:29:49 +0000 | [diff] [blame] | 168 | callingActivity != null ? callingActivity.getActivityToken() : null, |
| 169 | options); |
Jonathan Scott | d72fc8d | 2020-01-20 16:22:58 +0000 | [diff] [blame] | 170 | } catch (RemoteException ex) { |
| 171 | throw ex.rethrowFromSystemServer(); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /** |
Varun Shah | bdb849f | 2019-01-16 12:06:18 -0800 | [diff] [blame] | 176 | * Starts the specified activity of the caller package in the specified profile. Unlike |
| 177 | * {@link #startMainActivity}, this can start any activity of the caller package, not just |
| 178 | * the main activity. |
| 179 | * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} |
| 180 | * permission and both the caller and target user profiles must be in the same profile group. |
Varun Shah | acad138 | 2018-11-28 11:56:08 -0800 | [diff] [blame] | 181 | * |
| 182 | * @param component The ComponentName of the activity to launch. It must be exported. |
| 183 | * @param targetUser The UserHandle of the profile, must be one of the users returned by |
| 184 | * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will |
| 185 | * be thrown. |
| 186 | * @hide |
| 187 | */ |
| 188 | @SystemApi |
| 189 | @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) |
Varun Shah | bdb849f | 2019-01-16 12:06:18 -0800 | [diff] [blame] | 190 | public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { |
Varun Shah | acad138 | 2018-11-28 11:56:08 -0800 | [diff] [blame] | 191 | try { |
| 192 | mService.startActivityAsUser(mContext.getIApplicationThread(), |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame^] | 193 | mContext.getPackageName(), mContext.getAttributionTag(), component, |
Philip P. Moltmann | ee29509 | 2020-02-10 08:46:26 -0800 | [diff] [blame] | 194 | targetUser.getIdentifier(), false); |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 195 | } catch (RemoteException ex) { |
| 196 | throw ex.rethrowFromSystemServer(); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * Return a list of user profiles that that the caller can use when calling other APIs in this |
| 202 | * class. |
| 203 | * <p> |
| 204 | * A user profile would be considered as a valid target user profile, provided that: |
| 205 | * <ul> |
| 206 | * <li>It gets caller app installed</li> |
| 207 | * <li>It is not equal to the calling user</li> |
| 208 | * <li>It is in the same profile group of calling user profile</li> |
| 209 | * <li>It is enabled</li> |
| 210 | * </ul> |
| 211 | * |
| 212 | * @see UserManager#getUserProfiles() |
| 213 | */ |
| 214 | public @NonNull List<UserHandle> getTargetUserProfiles() { |
| 215 | try { |
| 216 | return mService.getTargetUserProfiles(mContext.getPackageName()); |
| 217 | } catch (RemoteException ex) { |
| 218 | throw ex.rethrowFromSystemServer(); |
| 219 | } |
| 220 | } |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 221 | |
| 222 | /** |
| 223 | * Return a label that calling app can show to user for the semantic of profile switching -- |
| 224 | * launching its own activity in specified user profile. For example, it may return |
| 225 | * "Switch to work" if the given user handle is the managed profile one. |
| 226 | * |
| 227 | * @param userHandle The UserHandle of the target profile, must be one of the users returned by |
| 228 | * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will |
| 229 | * be thrown. |
| 230 | * @return a label that calling app can show user for the semantic of launching its own |
| 231 | * activity in the specified user profile. |
| 232 | * |
Aurimas Liutikas | 7f69533 | 2018-05-31 21:07:32 -0700 | [diff] [blame] | 233 | * @see #startMainActivity(ComponentName, UserHandle) |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 234 | */ |
| 235 | public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) { |
| 236 | verifyCanAccessUser(userHandle); |
| 237 | |
| 238 | final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier()) |
| 239 | ? R.string.managed_profile_label |
| 240 | : R.string.user_owner_label; |
| 241 | return mResources.getString(stringRes); |
| 242 | } |
| 243 | |
| 244 | /** |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 245 | * Return a drawable that calling app can show to user for the semantic of profile switching -- |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 246 | * launching its own activity in specified user profile. For example, it may return a briefcase |
| 247 | * icon if the given user handle is the managed profile one. |
| 248 | * |
| 249 | * @param userHandle The UserHandle of the target profile, must be one of the users returned by |
| 250 | * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will |
| 251 | * be thrown. |
| 252 | * @return an icon that calling app can show user for the semantic of launching its own |
| 253 | * activity in specified user profile. |
| 254 | * |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 255 | * @see #startMainActivity(ComponentName, UserHandle) |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 256 | */ |
Tony Mak | b0d2262 | 2018-01-18 12:49:49 +0000 | [diff] [blame] | 257 | public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) { |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 258 | verifyCanAccessUser(userHandle); |
| 259 | |
| 260 | final boolean isManagedProfile = |
| 261 | mUserManager.isManagedProfile(userHandle.getIdentifier()); |
| 262 | if (isManagedProfile) { |
| 263 | return mResources.getDrawable(R.drawable.ic_corp_badge, null); |
| 264 | } else { |
| 265 | return UserIcons.getDefaultUserIcon( |
| 266 | mResources, UserHandle.USER_SYSTEM, true /* light */); |
| 267 | } |
| 268 | } |
| 269 | |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 270 | /** |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 271 | * Returns whether the calling package can request user consent to interact across profiles. |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 272 | * |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 273 | * <p>If {@code true}, user consent can be obtained via {@link |
| 274 | * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link |
| 275 | * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 276 | * |
| 277 | * <p>Specifically, returns whether the following are all true: |
| 278 | * <ul> |
| 279 | * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> |
| 280 | * <li>The calling app has requested</li> |
| 281 | * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. |
| 282 | * <li>The calling package has either been whitelisted by default by the OEM or has been |
| 283 | * explicitly whitelisted by the admin via |
| 284 | * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. |
| 285 | * </li> |
| 286 | * </ul> |
| 287 | * |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 288 | * <p>Note that user consent could already be granted if given a return value of {@code true}. |
| 289 | * The package's current ability to interact across profiles can be checked with {@link |
| 290 | * #canInteractAcrossProfiles()}. |
| 291 | * |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 292 | * @return true if the calling package can request to interact across profiles. |
| 293 | */ |
| 294 | public boolean canRequestInteractAcrossProfiles() { |
| 295 | try { |
| 296 | return mService.canRequestInteractAcrossProfiles(mContext.getPackageName()); |
| 297 | } catch (RemoteException ex) { |
| 298 | throw ex.rethrowFromSystemServer(); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Returns whether the calling package can interact across profiles. |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 304 | |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 305 | * <p>Specifically, returns whether the following are all true: |
| 306 | * <ul> |
| 307 | * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> |
| 308 | * <li>The user has previously consented to cross-profile communication for the calling |
| 309 | * package.</li> |
| 310 | * <li>The calling package has either been whitelisted by default by the OEM or has been |
| 311 | * explicitly whitelisted by the admin via |
| 312 | * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. |
| 313 | * </li> |
| 314 | * </ul> |
| 315 | * |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 316 | * <p>If {@code false}, the package's current ability to request user consent to interact across |
| 317 | * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true}, |
| 318 | * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The |
| 319 | * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. |
| 320 | * |
kholoud mohamed | 946df39 | 2019-12-12 17:43:32 +0000 | [diff] [blame] | 321 | * @return true if the calling package can interact across profiles. |
| 322 | * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the |
| 323 | * calling UID. |
| 324 | */ |
| 325 | public boolean canInteractAcrossProfiles() { |
| 326 | try { |
| 327 | return mService.canInteractAcrossProfiles(mContext.getPackageName()); |
| 328 | } catch (RemoteException ex) { |
| 329 | throw ex.rethrowFromSystemServer(); |
| 330 | } |
| 331 | } |
| 332 | |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 333 | /** |
| 334 | * Returns an {@link Intent} to open the settings page that allows the user to decide whether |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 335 | * the calling app can interact across profiles. |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 336 | * |
| 337 | * <p>Returns {@code null} if {@link #canRequestInteractAcrossProfiles()} is {@code false}. |
| 338 | * |
Alex Kershaw | c4b5cc5 | 2020-02-13 19:10:08 +0000 | [diff] [blame] | 339 | * <p>Note that the user may already have given consent and the app may already be able to |
| 340 | * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code |
| 341 | * true}. The current ability to interact across profiles is given by {@link |
| 342 | * #canInteractAcrossProfiles()}. |
| 343 | * |
kholoud mohamed | 5b2dfe3 | 2020-01-02 11:55:22 +0000 | [diff] [blame] | 344 | * @return an {@link Intent} to open the settings page that allows the user to decide whether |
| 345 | * the app can interact across profiles |
| 346 | * |
| 347 | * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the |
| 348 | * calling UID. |
| 349 | */ |
| 350 | public @Nullable Intent createRequestInteractAcrossProfilesIntent() { |
| 351 | if (!canRequestInteractAcrossProfiles()) { |
| 352 | return null; |
| 353 | } |
| 354 | final Intent settingsIntent = new Intent(); |
| 355 | settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS); |
| 356 | final Uri packageUri = Uri.parse("package:" + mContext.getPackageName()); |
| 357 | settingsIntent.setData(packageUri); |
| 358 | return settingsIntent; |
| 359 | } |
| 360 | |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 361 | /** |
| 362 | * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is |
| 363 | * configurable by users in Settings. This configures it for the profile group of the calling |
| 364 | * package. |
| 365 | * |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 366 | * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call |
| 367 | * if it is {@code false}. If presenting a user interface, do not allow the user to configure |
| 368 | * the app-op in that case. |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 369 | * |
| 370 | * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should |
| 371 | * never be set directly. This method ensures that the app-op is kept in sync for the app across |
| 372 | * each user in the profile group and that those apps are sent a broadcast when their ability to |
| 373 | * interact across profiles changes. |
| 374 | * |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 375 | * <p>This method should be used directly whenever a user's action results in a change in an |
| 376 | * app's ability to interact across profiles, as defined by the return value of {@link |
| 377 | * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during |
| 378 | * provisioning. |
| 379 | * |
| 380 | * <p>If other changes could have affected the app's ability to interact across profiles, as |
| 381 | * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the |
Alex Kershaw | 0124a09 | 2020-02-07 14:02:48 +0000 | [diff] [blame] | 382 | * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection, |
| 383 | * Set)} should be used. |
| 384 | * |
| 385 | * <p>If the caller does not have the {@link android.Manifest.permission |
| 386 | * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that |
| 387 | * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, |
| 388 | * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. |
| 389 | * |
| 390 | * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link |
| 391 | * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 392 | * |
| 393 | * @hide |
| 394 | */ |
| 395 | @RequiresPermission( |
Alex Kershaw | 0124a09 | 2020-02-07 14:02:48 +0000 | [diff] [blame] | 396 | allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, |
Alex Kershaw | 6fb8a02 | 2020-01-09 11:33:56 +0000 | [diff] [blame] | 397 | android.Manifest.permission.INTERACT_ACROSS_USERS}) |
| 398 | public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { |
| 399 | try { |
| 400 | mService.setInteractAcrossProfilesAppOp(packageName, newMode); |
| 401 | } catch (RemoteException ex) { |
| 402 | throw ex.rethrowFromSystemServer(); |
| 403 | } |
| 404 | } |
| 405 | |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 406 | /** |
| 407 | * Returns whether the given package can have its ability to interact across profiles configured |
| 408 | * by the user. This means that every other condition to interact across profiles has been set. |
| 409 | * |
| 410 | * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return |
| 411 | * {@code false} simply when the target profile is disabled. |
| 412 | * |
| 413 | * @hide |
| 414 | */ |
| 415 | public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) { |
| 416 | try { |
| 417 | return mService.canConfigureInteractAcrossProfiles(packageName); |
| 418 | } catch (RemoteException ex) { |
| 419 | throw ex.rethrowFromSystemServer(); |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | /** |
| 424 | * For each of the packages defined in {@code previousCrossProfilePackages} but not included in |
| 425 | * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission |
| 426 | * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by |
| 427 | * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}. |
| 428 | * |
| 429 | * <p>This method should be used whenever an app's ability to interact across profiles could |
| 430 | * have changed as a result of non-user actions, such as changes to admin or OEM consent |
| 431 | * whitelists. |
| 432 | * |
Alex Kershaw | 0124a09 | 2020-02-07 14:02:48 +0000 | [diff] [blame] | 433 | * <p>If the caller does not have the {@link android.Manifest.permission |
| 434 | * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that |
| 435 | * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, |
| 436 | * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. |
| 437 | * |
| 438 | * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link |
| 439 | * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. |
| 440 | * |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 441 | * @hide |
| 442 | */ |
| 443 | @RequiresPermission( |
Alex Kershaw | 0124a09 | 2020-02-07 14:02:48 +0000 | [diff] [blame] | 444 | allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, |
Alex Kershaw | 4b0197d | 2020-01-15 20:00:05 +0000 | [diff] [blame] | 445 | android.Manifest.permission.INTERACT_ACROSS_USERS}) |
| 446 | public void resetInteractAcrossProfilesAppOps( |
| 447 | @NonNull Collection<String> previousCrossProfilePackages, |
| 448 | @NonNull Set<String> newCrossProfilePackages) { |
| 449 | if (previousCrossProfilePackages.isEmpty()) { |
| 450 | return; |
| 451 | } |
| 452 | final List<String> unsetCrossProfilePackages = |
| 453 | previousCrossProfilePackages.stream() |
| 454 | .filter(packageName -> !newCrossProfilePackages.contains(packageName)) |
| 455 | .collect(Collectors.toList()); |
| 456 | if (unsetCrossProfilePackages.isEmpty()) { |
| 457 | return; |
| 458 | } |
| 459 | try { |
| 460 | mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages); |
| 461 | } catch (RemoteException ex) { |
| 462 | throw ex.rethrowFromSystemServer(); |
| 463 | } |
| 464 | } |
| 465 | |
Tony Mak | 8246513 | 2017-11-29 21:00:30 +0000 | [diff] [blame] | 466 | private void verifyCanAccessUser(UserHandle userHandle) { |
| 467 | if (!getTargetUserProfiles().contains(userHandle)) { |
| 468 | throw new SecurityException("Not allowed to access " + userHandle); |
| 469 | } |
| 470 | } |
Tony Mak | 1b708e6 | 2017-10-12 10:59:11 +0100 | [diff] [blame] | 471 | } |