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