1 /*
2  * Copyright (C) 2019 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 
17 package android.app.role;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.NonNull;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemService;
24 import android.annotation.TestApi;
25 import android.app.ActivityThread;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.RemoteCallback;
35 import android.util.Log;
36 import android.util.SparseArray;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.internal.infra.AndroidFuture;
40 import com.android.internal.infra.ServiceConnector;
41 
42 import java.util.concurrent.Executor;
43 import java.util.concurrent.TimeUnit;
44 import java.util.function.Consumer;
45 
46 /**
47  * Interface for communicating with the role controller.
48  *
49  * @hide
50  */
51 @SystemService(Context.ROLE_CONTROLLER_SERVICE)
52 @TestApi
53 public class RoleControllerManager {
54 
55     private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
56 
57     private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
58 
59     private static volatile ComponentName sRemoteServiceComponentName;
60 
61     private static final Object sRemoteServicesLock = new Object();
62 
63     /**
64      * Global remote services (per user) used by all {@link RoleControllerManager managers}.
65      */
66     @GuardedBy("sRemoteServicesLock")
67     private static final SparseArray<ServiceConnector<IRoleController>> sRemoteServices =
68             new SparseArray<>();
69 
70     @NonNull
71     private final ServiceConnector<IRoleController> mRemoteService;
72 
73     /**
74      * Initialize the remote service component name once so that we can avoid acquiring the
75      * PackageManagerService lock in constructor.
76      *
77      * @see #createWithInitializedRemoteServiceComponentName(Handler, Context)
78      *
79      * @hide
80      */
initializeRemoteServiceComponentName(@onNull Context context)81     public static void initializeRemoteServiceComponentName(@NonNull Context context) {
82         sRemoteServiceComponentName = getRemoteServiceComponentName(context);
83     }
84 
85     /**
86      * Create a {@link RoleControllerManager} instance with the initialized remote service component
87      * name so that we can avoid acquiring the PackageManagerService lock in constructor.
88      *
89      * @see #initializeRemoteServiceComponentName(Context)
90      *
91      * @hide
92      */
93     @NonNull
createWithInitializedRemoteServiceComponentName( @onNull Handler handler, @NonNull Context context)94     public static RoleControllerManager createWithInitializedRemoteServiceComponentName(
95             @NonNull Handler handler, @NonNull Context context) {
96         return new RoleControllerManager(sRemoteServiceComponentName, handler, context);
97     }
98 
RoleControllerManager(@onNull ComponentName remoteServiceComponentName, @NonNull Handler handler, @NonNull Context context)99     private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName,
100             @NonNull Handler handler, @NonNull Context context) {
101         synchronized (sRemoteServicesLock) {
102             int userId = context.getUserId();
103             ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId);
104             if (remoteService == null) {
105                 remoteService = new ServiceConnector.Impl<IRoleController>(
106                         ActivityThread.currentApplication(),
107                         new Intent(RoleControllerService.SERVICE_INTERFACE)
108                                 .setComponent(remoteServiceComponentName),
109                         0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) {
110 
111                     @Override
112                     protected Handler getJobHandler() {
113                         return handler;
114                     }
115                 };
116                 sRemoteServices.put(userId, remoteService);
117             }
118             mRemoteService = remoteService;
119         }
120     }
121 
122     /**
123      * @hide
124      */
RoleControllerManager(@onNull Context context)125     public RoleControllerManager(@NonNull Context context) {
126         this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context);
127     }
128 
129     @NonNull
getRemoteServiceComponentName(@onNull Context context)130     private static ComponentName getRemoteServiceComponentName(@NonNull Context context) {
131         Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
132         PackageManager packageManager = context.getPackageManager();
133         intent.setPackage(packageManager.getPermissionControllerPackageName());
134         ResolveInfo resolveInfo = packageManager.resolveService(intent, 0);
135         return resolveInfo.getComponentInfo().getComponentName();
136     }
137 
138     /**
139      * @see RoleControllerService#onGrantDefaultRoles()
140      *
141      * @hide
142      */
grantDefaultRoles(@onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)143     public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
144             @NonNull Consumer<Boolean> callback) {
145         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
146             AndroidFuture<Bundle> future = new AndroidFuture<>();
147             service.grantDefaultRoles(new RemoteCallback(future::complete));
148             return future;
149         });
150         propagateCallback(operation, "grantDefaultRoles", executor, callback);
151     }
152 
153     /**
154      * @see RoleControllerService#onAddRoleHolder(String, String, int)
155      *
156      * @hide
157      */
onAddRoleHolder(@onNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback)158     public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
159             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
160         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
161             AndroidFuture<Bundle> future = new AndroidFuture<>();
162             service.onAddRoleHolder(roleName, packageName, flags,
163                     new RemoteCallback(future::complete));
164             return future;
165         });
166         propagateCallback(operation, "onAddRoleHolder", callback);
167     }
168 
169     /**
170      * @see RoleControllerService#onRemoveRoleHolder(String, String, int)
171      *
172      * @hide
173      */
onRemoveRoleHolder(@onNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback)174     public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
175             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
176         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
177             AndroidFuture<Bundle> future = new AndroidFuture<>();
178             service.onRemoveRoleHolder(roleName, packageName, flags,
179                     new RemoteCallback(future::complete));
180             return future;
181         });
182         propagateCallback(operation, "onRemoveRoleHolder", callback);
183     }
184 
185     /**
186      * @see RoleControllerService#onClearRoleHolders(String, int)
187      *
188      * @hide
189      */
onClearRoleHolders(@onNull String roleName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback)190     public void onClearRoleHolders(@NonNull String roleName,
191             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
192         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
193             AndroidFuture<Bundle> future = new AndroidFuture<>();
194             service.onClearRoleHolders(roleName, flags,
195                     new RemoteCallback(future::complete));
196             return future;
197         });
198         propagateCallback(operation, "onClearRoleHolders", callback);
199     }
200 
201     /**
202      * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String)
203      *
204      * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)}
205      *             instead.
206      *
207      * @hide
208      */
209     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
isApplicationQualifiedForRole(@onNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)210     public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName,
211             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
212         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
213             AndroidFuture<Bundle> future = new AndroidFuture<>();
214             service.isApplicationQualifiedForRole(roleName, packageName,
215                     new RemoteCallback(future::complete));
216             return future;
217         });
218         propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback);
219     }
220 
221     /**
222      * @see RoleControllerService#onIsApplicationVisibleForRole(String, String)
223      *
224      * @hide
225      */
226     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
227     @TestApi
isApplicationVisibleForRole(@onNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)228     public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
229             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
230         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
231             AndroidFuture<Bundle> future = new AndroidFuture<>();
232             service.isApplicationVisibleForRole(roleName, packageName,
233                     new RemoteCallback(future::complete));
234             return future;
235         });
236         propagateCallback(operation, "isApplicationVisibleForRole", executor, callback);
237     }
238 
239     /**
240      * @see RoleControllerService#onIsRoleVisible(String)
241      *
242      * @hide
243      */
244     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
245     @TestApi
isRoleVisible(@onNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)246     public void isRoleVisible(@NonNull String roleName,
247             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
248         AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
249             AndroidFuture<Bundle> future = new AndroidFuture<>();
250             service.isRoleVisible(roleName, new RemoteCallback(future::complete));
251             return future;
252         });
253         propagateCallback(operation, "isRoleVisible", executor, callback);
254     }
255 
propagateCallback(AndroidFuture<Bundle> operation, String opName, @CallbackExecutor @NonNull Executor executor, Consumer<Boolean> destination)256     private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
257             @CallbackExecutor @NonNull Executor executor,
258             Consumer<Boolean> destination) {
259         operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
260                 .whenComplete((res, err) -> executor.execute(() -> {
261                     long token = Binder.clearCallingIdentity();
262                     try {
263                         if (err != null) {
264                             Log.e(LOG_TAG, "Error calling " + opName + "()", err);
265                             destination.accept(false);
266                         } else {
267                             destination.accept(res != null);
268                         }
269                     } finally {
270                         Binder.restoreCallingIdentity(token);
271                     }
272                 }));
273     }
274 
propagateCallback(AndroidFuture<Bundle> operation, String opName, RemoteCallback destination)275     private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
276             RemoteCallback destination) {
277         operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
278                 .whenComplete((res, err) -> {
279                     long token = Binder.clearCallingIdentity();
280                     try {
281                         if (err != null) {
282                             Log.e(LOG_TAG, "Error calling " + opName + "()", err);
283                             destination.sendResult(null);
284                         } else {
285                             destination.sendResult(res);
286                         }
287                     } finally {
288                         Binder.restoreCallingIdentity(token);
289                     }
290                 });
291     }
292 }
293