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