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