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