blob: 668dbd4537ff6843f256845d346dee059a8dc087 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.role;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.infra.AbstractRemoteService;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Interface for communicating with the role controller.
*
* @hide
*/
@SystemService(Context.ROLE_CONTROLLER_SERVICE)
public class RoleControllerManager {
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
private static volatile ComponentName sRemoteServiceComponentName;
private static final Object sRemoteServicesLock = new Object();
/**
* Global remote services (per user) used by all {@link RoleControllerManager managers}.
*/
@GuardedBy("sRemoteServicesLock")
private static final SparseArray<RemoteService> sRemoteServices = new SparseArray<>();
@NonNull
private final RemoteService mRemoteService;
/**
* Initialize the remote service component name once so that we can avoid acquiring the
* PackageManagerService lock in constructor.
*
* @see #createWithInitializedRemoteServiceComponentName(Handler, Context)
*/
public static void initializeRemoteServiceComponentName(@NonNull Context context) {
sRemoteServiceComponentName = getRemoteServiceComponentName(context);
}
/**
* Create a {@link RoleControllerManager} instance with the initialized remote service component
* name so that we can avoid acquiring the PackageManagerService lock in constructor.
*
* @see #initializeRemoteServiceComponentName(Context)
*/
@NonNull
public static RoleControllerManager createWithInitializedRemoteServiceComponentName(
@NonNull Handler handler, @NonNull Context context) {
return new RoleControllerManager(sRemoteServiceComponentName, handler, context);
}
private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName,
@NonNull Handler handler, @NonNull Context context) {
synchronized (sRemoteServicesLock) {
int userId = context.getUserId();
RemoteService remoteService = sRemoteServices.get(userId);
if (remoteService == null) {
remoteService = new RemoteService(ActivityThread.currentApplication(),
remoteServiceComponentName, handler, userId);
sRemoteServices.put(userId, remoteService);
}
mRemoteService = remoteService;
}
}
public RoleControllerManager(@NonNull Context context) {
this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context);
}
@NonNull
private static ComponentName getRemoteServiceComponentName(@NonNull Context context) {
Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
PackageManager packageManager = context.getPackageManager();
intent.setPackage(packageManager.getPermissionControllerPackageName());
ResolveInfo resolveInfo = packageManager.resolveService(intent, 0);
return resolveInfo.getComponentInfo().getComponentName();
}
/**
* @see RoleControllerService#onGrantDefaultRoles()
*/
public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
mRemoteService.scheduleRequest(new GrantDefaultRolesRequest(mRemoteService, executor,
callback));
}
/**
* @see RoleControllerService#onAddRoleHolder(String, String, int)
*/
public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
mRemoteService.scheduleRequest(new OnAddRoleHolderRequest(mRemoteService, roleName,
packageName, flags, callback));
}
/**
* @see RoleControllerService#onRemoveRoleHolder(String, String, int)
*/
public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
mRemoteService.scheduleRequest(new OnRemoveRoleHolderRequest(mRemoteService, roleName,
packageName, flags, callback));
}
/**
* @see RoleControllerService#onClearRoleHolders(String, int)
*/
public void onClearRoleHolders(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
mRemoteService.scheduleRequest(new OnClearRoleHoldersRequest(mRemoteService, roleName,
flags, callback));
}
/**
* @see RoleControllerService#onIsApplicationQualifiedForRole(String, String)
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
mRemoteService.scheduleRequest(new IsApplicationQualifiedForRoleRequest(mRemoteService,
roleName, packageName, executor, callback));
}
/**
* @see RoleControllerService#onIsRoleVisible(String)
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
public void isRoleVisible(@NonNull String roleName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
mRemoteService.scheduleRequest(new IsRoleVisibleRequest(mRemoteService, roleName, executor,
callback));
}
/**
* Connection to the remote service.
*/
private static final class RemoteService extends AbstractMultiplePendingRequestsRemoteService<
RemoteService, IRoleController> {
private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
/**
* Create a connection to the remote service
*
* @param context the context to use
* @param componentName the component of the service to connect to
* @param handler the handler for binding service and callbacks
* @param userId the user whom remote service should be connected as
*/
RemoteService(@NonNull Context context, @NonNull ComponentName componentName,
@NonNull Handler handler, @UserIdInt int userId) {
super(context, RoleControllerService.SERVICE_INTERFACE, componentName, userId,
service -> Log.e(LOG_TAG, "RemoteService " + service + " died"), handler, 0,
false, 1);
}
/**
* @return The default handler used by this service.
*/
@NonNull
public Handler getHandler() {
return mHandler;
}
@Override
protected @NonNull IRoleController getServiceInterface(@NonNull IBinder binder) {
return IRoleController.Stub.asInterface(binder);
}
@Override
protected long getTimeoutIdleBindMillis() {
return UNBIND_DELAY_MILLIS;
}
@Override
protected long getRemoteRequestMillis() {
return REQUEST_TIMEOUT_MILLIS;
}
@Override
public void scheduleRequest(
@NonNull BasePendingRequest<RemoteService, IRoleController> pendingRequest) {
super.scheduleRequest(pendingRequest);
}
@Override
public void scheduleAsyncRequest(@NonNull AsyncRequest<IRoleController> request) {
super.scheduleAsyncRequest(request);
}
}
/**
* Request for {@link #grantDefaultRoles(Executor, Consumer)}.
*/
private static final class GrantDefaultRolesRequest
extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final Executor mExecutor;
@NonNull
private final Consumer<Boolean> mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private GrantDefaultRolesRequest(@NonNull RemoteService service,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
super(service);
mExecutor = executor;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
long token = Binder.clearCallingIdentity();
try {
boolean successful = result != null;
mCallback.accept(successful);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
}));
}
@Override
protected void onTimeout(@NonNull RemoteService remoteService) {
mExecutor.execute(() -> mCallback.accept(false));
}
@Override
public void run() {
try {
getService().getServiceInterface().grantDefaultRoles(mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling grantDefaultRoles()", e);
}
}
@Override
protected void onFailed() {
mRemoteCallback.sendResult(null);
}
}
/**
* Request for {@link #onAddRoleHolder(String, String, int, RemoteCallback)}.
*/
private static final class OnAddRoleHolderRequest
extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final String mRoleName;
@NonNull
private final String mPackageName;
@RoleManager.ManageHoldersFlags
private final int mFlags;
@NonNull
private final RemoteCallback mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private OnAddRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
@NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
@NonNull RemoteCallback callback) {
super(service);
mRoleName = roleName;
mPackageName = packageName;
mFlags = flags;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> {
long token = Binder.clearCallingIdentity();
try {
mCallback.sendResult(result);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
});
}
@Override
protected void onTimeout(@NonNull RemoteService remoteService) {
mCallback.sendResult(null);
}
@Override
public void run() {
try {
getService().getServiceInterface().onAddRoleHolder(mRoleName, mPackageName, mFlags,
mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling onAddRoleHolder()", e);
}
}
}
/**
* Request for {@link #onRemoveRoleHolder(String, String, int, RemoteCallback)}.
*/
private static final class OnRemoveRoleHolderRequest
extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final String mRoleName;
@NonNull
private final String mPackageName;
@RoleManager.ManageHoldersFlags
private final int mFlags;
@NonNull
private final RemoteCallback mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private OnRemoveRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
@NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
@NonNull RemoteCallback callback) {
super(service);
mRoleName = roleName;
mPackageName = packageName;
mFlags = flags;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> {
long token = Binder.clearCallingIdentity();
try {
mCallback.sendResult(result);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
});
}
@Override
protected void onTimeout(@NonNull RemoteService remoteService) {
mCallback.sendResult(null);
}
@Override
public void run() {
try {
getService().getServiceInterface().onRemoveRoleHolder(mRoleName, mPackageName,
mFlags, mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling onRemoveRoleHolder()", e);
}
}
}
/**
* Request for {@link #onClearRoleHolders(String, int, RemoteCallback)}.
*/
private static final class OnClearRoleHoldersRequest
extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final String mRoleName;
@RoleManager.ManageHoldersFlags
private final int mFlags;
@NonNull
private final RemoteCallback mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private OnClearRoleHoldersRequest(@NonNull RemoteService service, @NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
super(service);
mRoleName = roleName;
mFlags = flags;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> {
long token = Binder.clearCallingIdentity();
try {
mCallback.sendResult(result);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
});
}
@Override
protected void onTimeout(@NonNull RemoteService remoteService) {
mCallback.sendResult(null);
}
@Override
public void run() {
try {
getService().getServiceInterface().onClearRoleHolders(mRoleName, mFlags,
mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling onClearRoleHolders()", e);
}
}
}
/**
* Request for {@link #isApplicationQualifiedForRole(String, String, Executor, Consumer)}
*/
private static final class IsApplicationQualifiedForRoleRequest extends
AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final String mRoleName;
@NonNull
private final String mPackageName;
@NonNull
private final Executor mExecutor;
@NonNull
private final Consumer<Boolean> mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private IsApplicationQualifiedForRoleRequest(@NonNull RemoteService service,
@NonNull String roleName, @NonNull String packageName,
@CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
super(service);
mRoleName = roleName;
mPackageName = packageName;
mExecutor = executor;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
long token = Binder.clearCallingIdentity();
try {
boolean qualified = result != null;
mCallback.accept(qualified);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
}));
}
@Override
protected void onTimeout(RemoteService remoteService) {
mExecutor.execute(() -> mCallback.accept(false));
}
@Override
public void run() {
try {
getService().getServiceInterface().isApplicationQualifiedForRole(mRoleName,
mPackageName, mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling isApplicationQualifiedForRole()", e);
}
}
}
/**
* Request for {@link #isRoleVisible(String, Executor, Consumer)}
*/
private static final class IsRoleVisibleRequest
extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
@NonNull
private final String mRoleName;
@NonNull
private final Executor mExecutor;
@NonNull
private final Consumer<Boolean> mCallback;
@NonNull
private final RemoteCallback mRemoteCallback;
private IsRoleVisibleRequest(@NonNull RemoteService service, @NonNull String roleName,
@CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
super(service);
mRoleName = roleName;
mExecutor = executor;
mCallback = callback;
mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
long token = Binder.clearCallingIdentity();
try {
boolean visible = result != null;
mCallback.accept(visible);
} finally {
Binder.restoreCallingIdentity(token);
finish();
}
}));
}
@Override
protected void onTimeout(RemoteService remoteService) {
mExecutor.execute(() -> mCallback.accept(false));
}
@Override
public void run() {
try {
getService().getServiceInterface().isRoleVisible(mRoleName, mRemoteCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error calling isRoleVisible()", e);
}
}
}
}