blob: 38c05380925dfd14d85519c4615e7d439fee32ac [file] [log] [blame]
Hai Zhangb7776682018-09-25 15:10:57 -07001/*
2 * Copyright (C) 2018 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 android.app.role;
18
19import android.Manifest;
20import android.annotation.CallbackExecutor;
21import android.annotation.NonNull;
22import android.annotation.RequiresPermission;
23import android.annotation.SystemApi;
24import android.annotation.SystemService;
Hai Zhangb7776682018-09-25 15:10:57 -070025import android.content.Context;
26import android.content.Intent;
27import android.os.Binder;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.os.UserHandle;
Hai Zhangb7776682018-09-25 15:10:57 -070031
32import com.android.internal.util.Preconditions;
33
34import java.util.List;
Hai Zhangb7776682018-09-25 15:10:57 -070035import java.util.concurrent.Executor;
36
37/**
38 * This class provides information about and manages roles.
39 * <p>
40 * A role is a unique name within the system associated with certain privileges. The list of
41 * available roles might change with a system app update, so apps should not make assumption about
42 * the availability of roles. Instead, they should always query if the role is available using
43 * {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names
44 * are available as constants in this class, and a list of possibly available roles can be found in
45 * the AndroidX Libraries.
46 * <p>
47 * There can be multiple applications qualifying for a role, but only a subset of them can become
48 * role holders. To qualify for a role, an application must meet certain requirements, including
49 * defining certain components in its manifest. These requirements can be found in the AndroidX
50 * Libraries. Then the application will need user consent to become a role holder, which can be
Hai Zhang87ed09a2018-10-22 10:43:31 -070051 * requested using {@link android.app.Activity#startActivityForResult(Intent, int)} with the
52 * {@code Intent} obtained from {@link #createRequestRoleIntent(String)}.
Hai Zhangb7776682018-09-25 15:10:57 -070053 * <p>
54 * Upon becoming a role holder, the application may be granted certain privileges that are role
55 * specific. When the application loses its role, these privileges will also be revoked.
56 */
57@SystemService(Context.ROLE_SERVICE)
58public final class RoleManager {
59
60 private static final String LOG_TAG = RoleManager.class.getSimpleName();
61
62 /**
63 * The name of the dialer role.
64 */
65 public static final String ROLE_DIALER = "android.app.role.DIALER";
66
67 /**
68 * The name of the SMS role.
Jeff Sharkey82338602018-11-18 17:53:02 -070069 *
70 * @see Intent#CATEGORY_APP_MESSAGING
Hai Zhangb7776682018-09-25 15:10:57 -070071 */
72 public static final String ROLE_SMS = "android.app.role.SMS";
73
74 /**
Jeff Sharkey82338602018-11-18 17:53:02 -070075 * The name of the browser role.
76 *
77 * @see Intent#CATEGORY_APP_BROWSER
78 */
79 public static final String ROLE_BROWSER = "android.app.role.BROWSER";
80
81 /**
82 * The name of the gallery role.
83 *
84 * @see Intent#CATEGORY_APP_GALLERY
85 */
86 public static final String ROLE_GALLERY = "android.app.role.GALLERY";
87
88 /**
89 * The name of the music player role.
90 *
91 * @see Intent#CATEGORY_APP_MUSIC
92 */
93 public static final String ROLE_MUSIC = "android.app.role.MUSIC";
94
95 /**
Hai Zhangb7776682018-09-25 15:10:57 -070096 * The action used to request user approval of a role for an application.
97 *
98 * @hide
99 */
100 public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
101
102 /**
103 * The name of the requested role.
104 * <p>
105 * <strong>Type:</strong> String
106 *
107 * @hide
108 */
109 @SystemApi
110 public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
111
Hai Zhang87ed09a2018-10-22 10:43:31 -0700112 /**
113 * The permission required to manage records of role holders in {@link RoleManager} directly.
114 *
115 * @hide
116 */
117 public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
118 "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
119
Hai Zhangb7776682018-09-25 15:10:57 -0700120 @NonNull
121 private final Context mContext;
122
123 @NonNull
124 private final IRoleManager mService;
125
126 /**
127 * @hide
128 */
129 public RoleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException {
130 mContext = context;
131 mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
132 Context.ROLE_SERVICE));
133 }
134
135 /**
Hai Zhang87ed09a2018-10-22 10:43:31 -0700136 * Returns an {@code Intent} suitable for passing to
137 * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to
138 * grant a role to this application.
Hai Zhangb7776682018-09-25 15:10:57 -0700139 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700140 * If the role is granted, the {@code resultCode} will be
141 * {@link android.app.Activity#RESULT_OK}, otherwise it will be
142 * {@link android.app.Activity#RESULT_CANCELED}.
Hai Zhangb7776682018-09-25 15:10:57 -0700143 *
144 * @param roleName the name of requested role
145 *
146 * @return the {@code Intent} to prompt user to grant the role
147 *
148 * @throws IllegalArgumentException if {@code role} is {@code null} or empty
149 */
150 @NonNull
151 public Intent createRequestRoleIntent(@NonNull String roleName) {
152 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
153 Intent intent = new Intent(ACTION_REQUEST_ROLE);
154 intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
155 intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
156 return intent;
157 }
158
159 /**
160 * Check whether a role is available in the system.
161 *
162 * @param roleName the name of role to checking for
163 *
164 * @return whether the role is available in the system
165 *
166 * @throws IllegalArgumentException if the role name is {@code null} or empty
167 */
168 public boolean isRoleAvailable(@NonNull String roleName) {
169 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
170 try {
171 return mService.isRoleAvailable(roleName);
172 } catch (RemoteException e) {
173 throw e.rethrowFromSystemServer();
174 }
175 }
176
177 /**
178 * Check whether the calling application is holding a particular role.
179 *
180 * @param roleName the name of the role to check for
181 *
182 * @return whether the calling application is holding the role
183 *
184 * @throws IllegalArgumentException if the role name is {@code null} or empty.
185 */
186 public boolean isRoleHeld(@NonNull String roleName) {
187 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
188 try {
189 return mService.isRoleHeld(roleName, mContext.getPackageName());
190 } catch (RemoteException e) {
191 throw e.rethrowFromSystemServer();
192 }
193 }
194
195 /**
196 * Get package names of the applications holding the role.
197 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700198 * <strong>Note:</strong> Using this API requires holding
199 * {@code android.permission.MANAGE_ROLE_HOLDERS}.
200 *
201 * @param roleName the name of the role to get the role holder for
202 *
203 * @return a list of package names of the role holders, or an empty list if none.
204 *
205 * @throws IllegalArgumentException if the role name is {@code null} or empty.
206 *
207 * @see #getRoleHoldersAsUser(String, UserHandle)
208 *
209 * @hide
210 */
211 @NonNull
212 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
213 @SystemApi
214 public List<String> getRoleHolders(@NonNull String roleName) {
215 return getRoleHoldersAsUser(roleName, UserHandle.of(UserHandle.getCallingUserId()));
216 }
217
218 /**
219 * Get package names of the applications holding the role.
220 * <p>
221 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700222 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
223 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
224 *
225 * @param roleName the name of the role to get the role holder for
226 * @param user the user to get the role holder for
227 *
Hai Zhang87ed09a2018-10-22 10:43:31 -0700228 * @return a list of package names of the role holders, or an empty list if none.
Hai Zhangb7776682018-09-25 15:10:57 -0700229 *
230 * @throws IllegalArgumentException if the role name is {@code null} or empty.
231 *
232 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
233 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
234 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
235 *
236 * @hide
237 */
238 @NonNull
239 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
240 @SystemApi
Hai Zhang87ed09a2018-10-22 10:43:31 -0700241 public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
Hai Zhangb7776682018-09-25 15:10:57 -0700242 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
243 Preconditions.checkNotNull(user, "user cannot be null");
Hai Zhangb7776682018-09-25 15:10:57 -0700244 try {
Hai Zhang87ed09a2018-10-22 10:43:31 -0700245 return mService.getRoleHoldersAsUser(roleName, user.getIdentifier());
Hai Zhangb7776682018-09-25 15:10:57 -0700246 } catch (RemoteException e) {
247 throw e.rethrowFromSystemServer();
248 }
Hai Zhangb7776682018-09-25 15:10:57 -0700249 }
250
251 /**
252 * Add a specific application to the holders of a role. If the role is exclusive, the previous
253 * holder will be replaced.
254 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700255 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700256 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
257 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
258 *
259 * @param roleName the name of the role to add the role holder for
260 * @param packageName the package name of the application to add to the role holders
261 * @param user the user to add the role holder for
262 * @param executor the {@code Executor} to run the callback on.
263 * @param callback the callback for whether this call is successful
264 *
265 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
266 *
267 * @see #getRoleHoldersAsUser(String, UserHandle)
268 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
269 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
270 *
271 * @hide
272 */
273 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
274 @SystemApi
275 public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
276 @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
277 @NonNull RoleManagerCallback callback) {
278 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
279 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
280 Preconditions.checkNotNull(user, "user cannot be null");
281 Preconditions.checkNotNull(executor, "executor cannot be null");
282 Preconditions.checkNotNull(callback, "callback cannot be null");
283 try {
284 mService.addRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
285 new RoleManagerCallbackDelegate(executor, callback));
286 } catch (RemoteException e) {
287 throw e.rethrowFromSystemServer();
288 }
289 }
290
291 /**
292 * Remove a specific application from the holders of a role.
293 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700294 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700295 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
296 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
297 *
298 * @param roleName the name of the role to remove the role holder for
299 * @param packageName the package name of the application to remove from the role holders
300 * @param user the user to remove the role holder for
301 * @param executor the {@code Executor} to run the callback on.
302 * @param callback the callback for whether this call is successful
303 *
304 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
305 *
306 * @see #getRoleHoldersAsUser(String, UserHandle)
307 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
308 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
309 *
310 * @hide
311 */
312 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
313 @SystemApi
314 public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
315 @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
316 @NonNull RoleManagerCallback callback) {
317 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
318 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
319 Preconditions.checkNotNull(user, "user cannot be null");
320 Preconditions.checkNotNull(executor, "executor cannot be null");
321 Preconditions.checkNotNull(callback, "callback cannot be null");
322 try {
323 mService.removeRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
324 new RoleManagerCallbackDelegate(executor, callback));
325 } catch (RemoteException e) {
326 throw e.rethrowFromSystemServer();
327 }
328 }
329
330 /**
331 * Remove all holders of a role.
332 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700333 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700334 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
335 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
336 *
337 * @param roleName the name of the role to remove role holders for
338 * @param user the user to remove role holders for
339 * @param executor the {@code Executor} to run the callback on.
340 * @param callback the callback for whether this call is successful
341 *
342 * @throws IllegalArgumentException if the role name is {@code null} or empty.
343 *
344 * @see #getRoleHoldersAsUser(String, UserHandle)
345 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
346 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
347 *
348 * @hide
349 */
350 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
351 @SystemApi
352 public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
353 @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
354 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
355 Preconditions.checkNotNull(user, "user cannot be null");
356 Preconditions.checkNotNull(executor, "executor cannot be null");
357 Preconditions.checkNotNull(callback, "callback cannot be null");
358 try {
359 mService.clearRoleHoldersAsUser(roleName, user.getIdentifier(),
360 new RoleManagerCallbackDelegate(executor, callback));
361 } catch (RemoteException e) {
362 throw e.rethrowFromSystemServer();
363 }
364 }
365
Hai Zhang87ed09a2018-10-22 10:43:31 -0700366 /**
367 * Add a specific application to the holders of a role, only modifying records inside
368 * {@link RoleManager}. Should only be called from
369 * {@link android.rolecontrollerservice.RoleControllerService}.
370 * <p>
371 * <strong>Note:</strong> Using this API requires holding
372 * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
373 *
374 * @param roleName the name of the role to add the role holder for
375 * @param packageName the package name of the application to add to the role holders
376 *
377 * @return whether the operation was successful, and will also be {@code true} if a matching
378 * role holder is already found.
379 *
380 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
381 *
382 * @see #getRoleHolders(String)
383 * @see #removeRoleHolderFromController(String, String)
384 *
385 * @hide
386 */
387 @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
388 @SystemApi
389 public boolean addRoleHolderFromController(@NonNull String roleName,
390 @NonNull String packageName) {
391 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
392 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
393 try {
394 return mService.addRoleHolderFromController(roleName, packageName);
395 } catch (RemoteException e) {
396 throw e.rethrowFromSystemServer();
397 }
398 }
399
400 /**
401 * Remove a specific application from the holders of a role, only modifying records inside
402 * {@link RoleManager}. Should only be called from
403 * {@link android.rolecontrollerservice.RoleControllerService}.
404 * <p>
405 * <strong>Note:</strong> Using this API requires holding
406 * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
407 *
408 * @param roleName the name of the role to remove the role holder for
409 * @param packageName the package name of the application to remove from the role holders
410 *
411 * @return whether the operation was successful, and will also be {@code true} if no matching
412 * role holder was found to remove.
413 *
414 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
415 *
416 * @see #getRoleHolders(String)
417 * @see #addRoleHolderFromController(String, String)
418 *
419 * @hide
420 */
421 @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
422 @SystemApi
423 public boolean removeRoleHolderFromController(@NonNull String roleName,
424 @NonNull String packageName) {
425 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
426 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
427 try {
428 return mService.removeRoleHolderFromController(roleName, packageName);
429 } catch (RemoteException e) {
430 throw e.rethrowFromSystemServer();
431 }
432 }
433
Hai Zhangb7776682018-09-25 15:10:57 -0700434 private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
435
436 @NonNull
437 private final Executor mExecutor;
438 @NonNull
439 private final RoleManagerCallback mCallback;
440
441 RoleManagerCallbackDelegate(@NonNull Executor executor,
442 @NonNull RoleManagerCallback callback) {
443 mExecutor = executor;
444 mCallback = callback;
445 }
446
447 @Override
448 public void onSuccess() {
449 long token = Binder.clearCallingIdentity();
450 try {
451 mExecutor.execute(mCallback::onSuccess);
452 } finally {
453 Binder.restoreCallingIdentity(token);
454 }
455 }
456
457 @Override
458 public void onFailure() {
459 long token = Binder.clearCallingIdentity();
460 try {
461 mExecutor.execute(mCallback::onFailure);
462 } finally {
463 Binder.restoreCallingIdentity(token);
464 }
465 }
466 }
467}