blob: ed27d9fe9fd4fc52702fba14d19f612a53c3c28d [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.
69 */
70 public static final String ROLE_SMS = "android.app.role.SMS";
71
72 /**
73 * The action used to request user approval of a role for an application.
74 *
75 * @hide
76 */
77 public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
78
79 /**
80 * The name of the requested role.
81 * <p>
82 * <strong>Type:</strong> String
83 *
84 * @hide
85 */
86 @SystemApi
87 public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
88
Hai Zhang87ed09a2018-10-22 10:43:31 -070089 /**
90 * The permission required to manage records of role holders in {@link RoleManager} directly.
91 *
92 * @hide
93 */
94 public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
95 "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
96
Hai Zhangb7776682018-09-25 15:10:57 -070097 @NonNull
98 private final Context mContext;
99
100 @NonNull
101 private final IRoleManager mService;
102
103 /**
104 * @hide
105 */
106 public RoleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException {
107 mContext = context;
108 mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
109 Context.ROLE_SERVICE));
110 }
111
112 /**
Hai Zhang87ed09a2018-10-22 10:43:31 -0700113 * Returns an {@code Intent} suitable for passing to
114 * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to
115 * grant a role to this application.
Hai Zhangb7776682018-09-25 15:10:57 -0700116 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700117 * If the role is granted, the {@code resultCode} will be
118 * {@link android.app.Activity#RESULT_OK}, otherwise it will be
119 * {@link android.app.Activity#RESULT_CANCELED}.
Hai Zhangb7776682018-09-25 15:10:57 -0700120 *
121 * @param roleName the name of requested role
122 *
123 * @return the {@code Intent} to prompt user to grant the role
124 *
125 * @throws IllegalArgumentException if {@code role} is {@code null} or empty
126 */
127 @NonNull
128 public Intent createRequestRoleIntent(@NonNull String roleName) {
129 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
130 Intent intent = new Intent(ACTION_REQUEST_ROLE);
131 intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
132 intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
133 return intent;
134 }
135
136 /**
137 * Check whether a role is available in the system.
138 *
139 * @param roleName the name of role to checking for
140 *
141 * @return whether the role is available in the system
142 *
143 * @throws IllegalArgumentException if the role name is {@code null} or empty
144 */
145 public boolean isRoleAvailable(@NonNull String roleName) {
146 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
147 try {
148 return mService.isRoleAvailable(roleName);
149 } catch (RemoteException e) {
150 throw e.rethrowFromSystemServer();
151 }
152 }
153
154 /**
155 * Check whether the calling application is holding a particular role.
156 *
157 * @param roleName the name of the role to check for
158 *
159 * @return whether the calling application is holding the role
160 *
161 * @throws IllegalArgumentException if the role name is {@code null} or empty.
162 */
163 public boolean isRoleHeld(@NonNull String roleName) {
164 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
165 try {
166 return mService.isRoleHeld(roleName, mContext.getPackageName());
167 } catch (RemoteException e) {
168 throw e.rethrowFromSystemServer();
169 }
170 }
171
172 /**
173 * Get package names of the applications holding the role.
174 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700175 * <strong>Note:</strong> Using this API requires holding
176 * {@code android.permission.MANAGE_ROLE_HOLDERS}.
177 *
178 * @param roleName the name of the role to get the role holder for
179 *
180 * @return a list of package names of the role holders, or an empty list if none.
181 *
182 * @throws IllegalArgumentException if the role name is {@code null} or empty.
183 *
184 * @see #getRoleHoldersAsUser(String, UserHandle)
185 *
186 * @hide
187 */
188 @NonNull
189 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
190 @SystemApi
191 public List<String> getRoleHolders(@NonNull String roleName) {
192 return getRoleHoldersAsUser(roleName, UserHandle.of(UserHandle.getCallingUserId()));
193 }
194
195 /**
196 * Get package names of the applications holding the role.
197 * <p>
198 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700199 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
200 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
201 *
202 * @param roleName the name of the role to get the role holder for
203 * @param user the user to get the role holder for
204 *
Hai Zhang87ed09a2018-10-22 10:43:31 -0700205 * @return a list of package names of the role holders, or an empty list if none.
Hai Zhangb7776682018-09-25 15:10:57 -0700206 *
207 * @throws IllegalArgumentException if the role name is {@code null} or empty.
208 *
209 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
210 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
211 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
212 *
213 * @hide
214 */
215 @NonNull
216 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
217 @SystemApi
Hai Zhang87ed09a2018-10-22 10:43:31 -0700218 public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
Hai Zhangb7776682018-09-25 15:10:57 -0700219 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
220 Preconditions.checkNotNull(user, "user cannot be null");
Hai Zhangb7776682018-09-25 15:10:57 -0700221 try {
Hai Zhang87ed09a2018-10-22 10:43:31 -0700222 return mService.getRoleHoldersAsUser(roleName, user.getIdentifier());
Hai Zhangb7776682018-09-25 15:10:57 -0700223 } catch (RemoteException e) {
224 throw e.rethrowFromSystemServer();
225 }
Hai Zhangb7776682018-09-25 15:10:57 -0700226 }
227
228 /**
229 * Add a specific application to the holders of a role. If the role is exclusive, the previous
230 * holder will be replaced.
231 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700232 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700233 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
234 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
235 *
236 * @param roleName the name of the role to add the role holder for
237 * @param packageName the package name of the application to add to the role holders
238 * @param user the user to add the role holder for
239 * @param executor the {@code Executor} to run the callback on.
240 * @param callback the callback for whether this call is successful
241 *
242 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
243 *
244 * @see #getRoleHoldersAsUser(String, UserHandle)
245 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
246 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
247 *
248 * @hide
249 */
250 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
251 @SystemApi
252 public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
253 @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
254 @NonNull RoleManagerCallback callback) {
255 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
256 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
257 Preconditions.checkNotNull(user, "user cannot be null");
258 Preconditions.checkNotNull(executor, "executor cannot be null");
259 Preconditions.checkNotNull(callback, "callback cannot be null");
260 try {
261 mService.addRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
262 new RoleManagerCallbackDelegate(executor, callback));
263 } catch (RemoteException e) {
264 throw e.rethrowFromSystemServer();
265 }
266 }
267
268 /**
269 * Remove a specific application from the holders of a role.
270 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700271 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700272 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
273 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
274 *
275 * @param roleName the name of the role to remove the role holder for
276 * @param packageName the package name of the application to remove from the role holders
277 * @param user the user to remove the role holder for
278 * @param executor the {@code Executor} to run the callback on.
279 * @param callback the callback for whether this call is successful
280 *
281 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
282 *
283 * @see #getRoleHoldersAsUser(String, UserHandle)
284 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
285 * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
286 *
287 * @hide
288 */
289 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
290 @SystemApi
291 public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
292 @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
293 @NonNull RoleManagerCallback callback) {
294 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
295 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
296 Preconditions.checkNotNull(user, "user cannot be null");
297 Preconditions.checkNotNull(executor, "executor cannot be null");
298 Preconditions.checkNotNull(callback, "callback cannot be null");
299 try {
300 mService.removeRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
301 new RoleManagerCallbackDelegate(executor, callback));
302 } catch (RemoteException e) {
303 throw e.rethrowFromSystemServer();
304 }
305 }
306
307 /**
308 * Remove all holders of a role.
309 * <p>
Hai Zhang87ed09a2018-10-22 10:43:31 -0700310 * <strong>Note:</strong> Using this API requires holding
Hai Zhangb7776682018-09-25 15:10:57 -0700311 * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
312 * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
313 *
314 * @param roleName the name of the role to remove role holders for
315 * @param user the user to remove role holders for
316 * @param executor the {@code Executor} to run the callback on.
317 * @param callback the callback for whether this call is successful
318 *
319 * @throws IllegalArgumentException if the role name is {@code null} or empty.
320 *
321 * @see #getRoleHoldersAsUser(String, UserHandle)
322 * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
323 * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
324 *
325 * @hide
326 */
327 @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
328 @SystemApi
329 public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
330 @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
331 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
332 Preconditions.checkNotNull(user, "user cannot be null");
333 Preconditions.checkNotNull(executor, "executor cannot be null");
334 Preconditions.checkNotNull(callback, "callback cannot be null");
335 try {
336 mService.clearRoleHoldersAsUser(roleName, user.getIdentifier(),
337 new RoleManagerCallbackDelegate(executor, callback));
338 } catch (RemoteException e) {
339 throw e.rethrowFromSystemServer();
340 }
341 }
342
Hai Zhang87ed09a2018-10-22 10:43:31 -0700343 /**
344 * Add a specific application to the holders of a role, only modifying records inside
345 * {@link RoleManager}. Should only be called from
346 * {@link android.rolecontrollerservice.RoleControllerService}.
347 * <p>
348 * <strong>Note:</strong> Using this API requires holding
349 * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
350 *
351 * @param roleName the name of the role to add the role holder for
352 * @param packageName the package name of the application to add to the role holders
353 *
354 * @return whether the operation was successful, and will also be {@code true} if a matching
355 * role holder is already found.
356 *
357 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
358 *
359 * @see #getRoleHolders(String)
360 * @see #removeRoleHolderFromController(String, String)
361 *
362 * @hide
363 */
364 @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
365 @SystemApi
366 public boolean addRoleHolderFromController(@NonNull String roleName,
367 @NonNull String packageName) {
368 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
369 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
370 try {
371 return mService.addRoleHolderFromController(roleName, packageName);
372 } catch (RemoteException e) {
373 throw e.rethrowFromSystemServer();
374 }
375 }
376
377 /**
378 * Remove a specific application from the holders of a role, only modifying records inside
379 * {@link RoleManager}. Should only be called from
380 * {@link android.rolecontrollerservice.RoleControllerService}.
381 * <p>
382 * <strong>Note:</strong> Using this API requires holding
383 * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
384 *
385 * @param roleName the name of the role to remove the role holder for
386 * @param packageName the package name of the application to remove from the role holders
387 *
388 * @return whether the operation was successful, and will also be {@code true} if no matching
389 * role holder was found to remove.
390 *
391 * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
392 *
393 * @see #getRoleHolders(String)
394 * @see #addRoleHolderFromController(String, String)
395 *
396 * @hide
397 */
398 @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
399 @SystemApi
400 public boolean removeRoleHolderFromController(@NonNull String roleName,
401 @NonNull String packageName) {
402 Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
403 Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
404 try {
405 return mService.removeRoleHolderFromController(roleName, packageName);
406 } catch (RemoteException e) {
407 throw e.rethrowFromSystemServer();
408 }
409 }
410
Hai Zhangb7776682018-09-25 15:10:57 -0700411 private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
412
413 @NonNull
414 private final Executor mExecutor;
415 @NonNull
416 private final RoleManagerCallback mCallback;
417
418 RoleManagerCallbackDelegate(@NonNull Executor executor,
419 @NonNull RoleManagerCallback callback) {
420 mExecutor = executor;
421 mCallback = callback;
422 }
423
424 @Override
425 public void onSuccess() {
426 long token = Binder.clearCallingIdentity();
427 try {
428 mExecutor.execute(mCallback::onSuccess);
429 } finally {
430 Binder.restoreCallingIdentity(token);
431 }
432 }
433
434 @Override
435 public void onFailure() {
436 long token = Binder.clearCallingIdentity();
437 try {
438 mExecutor.execute(mCallback::onFailure);
439 } finally {
440 Binder.restoreCallingIdentity(token);
441 }
442 }
443 }
444}