blob: d92c95691eea19f23b993238d871613afb53a7b6 [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.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.UserHandle;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Abstract base class for the role controller service.
* <p>
* Subclass should implement the business logic for role management, including enforcing role
* requirements and granting or revoking relevant privileges of roles. This class can only be
* implemented by the permission controller app which is registered in {@code PackageManager}.
*
* @hide
*/
@SystemApi
public abstract class RoleControllerService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
*/
public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService";
private HandlerThread mWorkerThread;
private Handler mWorkerHandler;
@Override
public void onCreate() {
super.onCreate();
mWorkerThread = new HandlerThread(RoleControllerService.class.getSimpleName());
mWorkerThread.start();
mWorkerHandler = new Handler(mWorkerThread.getLooper());
}
@Override
public void onDestroy() {
super.onDestroy();
mWorkerThread.quitSafely();
}
@Nullable
@Override
public final IBinder onBind(@Nullable Intent intent) {
return new IRoleController.Stub() {
@Override
public void grantDefaultRoles(RemoteCallback callback) {
enforceCallerSystemUid("grantDefaultRoles");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
RoleControllerService::grantDefaultRoles, RoleControllerService.this,
callback));
}
@Override
public void onAddRoleHolder(String roleName, String packageName, int flags,
RemoteCallback callback) {
enforceCallerSystemUid("onAddRoleHolder");
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
RoleControllerService::onAddRoleHolder, RoleControllerService.this,
roleName, packageName, flags, callback));
}
@Override
public void onRemoveRoleHolder(String roleName, String packageName, int flags,
RemoteCallback callback) {
enforceCallerSystemUid("onRemoveRoleHolder");
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
RoleControllerService::onRemoveRoleHolder, RoleControllerService.this,
roleName, packageName, flags, callback));
}
@Override
public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) {
enforceCallerSystemUid("onClearRoleHolders");
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
RoleControllerService::onClearRoleHolders, RoleControllerService.this,
roleName, flags, callback));
}
private void enforceCallerSystemUid(@NonNull String methodName) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system process can call " + methodName
+ "()");
}
}
@Override
public void isApplicationQualifiedForRole(String roleName, String packageName,
RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
callback.sendResult(qualified ? Bundle.EMPTY : null);
}
@Override
public void isApplicationVisibleForRole(String roleName, String packageName,
RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName,
"packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
boolean visible = onIsApplicationVisibleForRole(roleName, packageName);
callback.sendResult(visible ? Bundle.EMPTY : null);
}
@Override
public void isRoleVisible(String roleName, RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
boolean visible = onIsRoleVisible(roleName);
callback.sendResult(visible ? Bundle.EMPTY : null);
}
};
}
private void grantDefaultRoles(@NonNull RemoteCallback callback) {
boolean successful = onGrantDefaultRoles();
callback.sendResult(successful ? Bundle.EMPTY : null);
}
private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
boolean successful = onAddRoleHolder(roleName, packageName, flags);
callback.sendResult(successful ? Bundle.EMPTY : null);
}
private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
boolean successful = onRemoveRoleHolder(roleName, packageName, flags);
callback.sendResult(successful ? Bundle.EMPTY : null);
}
private void onClearRoleHolders(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
boolean successful = onClearRoleHolders(roleName, flags);
callback.sendResult(successful ? Bundle.EMPTY : null);
}
/**
* Called by system to grant default permissions and roles.
* <p>
* This is typically when creating a new user or upgrading either system or
* permission controller package
*
* @return whether this call was successful
*/
@WorkerThread
public abstract boolean onGrantDefaultRoles();
/**
* Add a specific application to the holders of a role. If the role is exclusive, the previous
* holder will be replaced.
* <p>
* Implementation should enforce the role requirements and grant or revoke the relevant
* privileges of roles.
*
* @param roleName the name of the role to add the role holder for
* @param packageName the package name of the application to add to the role holders
* @param flags optional behavior flags
*
* @return whether this call was successful
*
* @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor,
* RemoteCallback)
*/
@WorkerThread
public abstract boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags);
/**
* Remove a specific application from the holders of a role.
*
* @param roleName the name of the role to remove the role holder for
* @param packageName the package name of the application to remove from the role holders
* @param flags optional behavior flags
*
* @return whether this call was successful
*
* @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor,
* RemoteCallback)
*/
@WorkerThread
public abstract boolean onRemoveRoleHolder(@NonNull String roleName,
@NonNull String packageName, @RoleManager.ManageHoldersFlags int flags);
/**
* Remove all holders of a role.
*
* @param roleName the name of the role to remove role holders for
* @param flags optional behavior flags
*
* @return whether this call was successful
*
* @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor, RemoteCallback)
*/
@WorkerThread
public abstract boolean onClearRoleHolders(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags);
/**
* Check whether an application is qualified for a role.
*
* @param roleName name of the role to check for
* @param packageName package name of the application to check for
*
* @return whether the application is qualified for the role
*
* @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead.
*/
public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
@NonNull String packageName);
/**
* Check whether an application is visible for a role.
*
* While an application can be qualified for a role, it can still stay hidden from user (thus
* not visible). If an application is visible for a role, we may show things related to the role
* for it, e.g. showing an entry pointing to the role settings in its application info page.
*
* @param roleName name of the role to check for
* @param packageName package name of the application to check for
*
* @return whether the application is visible for the role
*/
public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
@NonNull String packageName) {
return onIsApplicationQualifiedForRole(roleName, packageName);
}
/**
* Check whether a role should be visible to user.
*
* @param roleName name of the role to check for
*
* @return whether the role should be visible to user
*/
public abstract boolean onIsRoleVisible(@NonNull String roleName);
}