Merge "Implement "no-UI" flow in CDM"

This commit is contained in:
Sergey Nikolaienkov
2021-11-30 20:02:28 +00:00
committed by Android (Google) Code Review
4 changed files with 94 additions and 45 deletions

View File

@@ -20,10 +20,7 @@ import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.PermissionsUtils.enforceRequestDeviceProfilePermissions;
import static com.android.server.companion.PermissionsUtils.enforceRequestSelfManagedPermission;
import static com.android.server.companion.PermissionsUtils.enforceCallerPermissionsToRequest;
import static com.android.server.companion.RolesUtils.isRoleHolder;
import static java.util.Objects.requireNonNull;
@@ -81,13 +78,13 @@ class AssociationRequestsProcessor {
mService = service;
final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
mServiceConnectors = new PerUser<>() {
@Override
protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) {
return new ServiceConnector.Impl<>(
mContext,
serviceIntent, 0/* bindingFlags */, userId,
ICompanionDeviceDiscoveryService.Stub::asInterface);
mContext,
serviceIntent, 0/* bindingFlags */, userId,
ICompanionDeviceDiscoveryService.Stub::asInterface);
}
};
}
@@ -99,6 +96,10 @@ class AssociationRequestsProcessor {
void process(@NonNull AssociationRequest request, @NonNull String packageName,
@UserIdInt int userId, @NonNull IAssociationRequestCallback callback) {
requireNonNull(request, "Request MUST NOT be null");
if (request.isSelfManaged()) {
requireNonNull(request.getDisplayName(), "AssociationRequest.displayName "
+ "MUST NOT be null.");
}
requireNonNull(packageName, "Package name MUST NOT be null");
requireNonNull(callback, "Callback MUST NOT be null");
@@ -108,33 +109,33 @@ class AssociationRequestsProcessor {
+ "package=u" + userId + "/" + packageName);
}
enforceCallerCanInteractWithUserId(mContext, userId);
enforceCallerIsSystemOr(userId, packageName);
// 1. Enforce permissions and other requirements.
enforceCallerPermissionsToRequest(mContext, request, packageName, userId);
mService.checkUsesFeature(packageName, userId);
if (request.isSelfManaged()) {
enforceRequestSelfManagedPermission(mContext);
// 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
// to perform discovery NOR to collect user consent).
if (request.isSelfManaged() && !request.isForceConfirmation()
&& !willAddRoleHolder(request, packageName, userId)) {
// 2a. Create association right away.
final AssociationInfo association = mService.createAssociation(userId, packageName,
/* macAddress */ null, request.getDisplayName(), request.getDeviceProfile(),
/* selfManaged */true);
withCatchingRemoteException(() -> callback.onAssociationCreated(association));
return;
}
final String deviceProfile = request.getDeviceProfile();
enforceRequestDeviceProfilePermissions(mContext, deviceProfile);
// 2b. Launch the UI.
synchronized (mService.mLock) {
if (mRequest != null) {
Slog.w(TAG, "CDM is already processing another AssociationRequest.");
try {
callback.onFailure("Busy.");
} catch (RemoteException e) {
// OK to ignore.
}
return;
withCatchingRemoteException(() -> callback.onFailure("Busy."));
}
try {
callback.asBinder().linkToDeath(mBinderDeathRecipient /* recipient */, 0);
} catch (RemoteException e) {
final boolean linked = withCatchingRemoteException(
() -> callback.asBinder().linkToDeath(mBinderDeathRecipient, 0));
if (!linked) {
// The process has died by now: do not proceed.
return;
}
@@ -150,6 +151,7 @@ class AssociationRequestsProcessor {
request.setSkipPrompt(true);
}
final String deviceProfile = request.getDeviceProfile();
mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile)
.thenComposeAsync(description -> {
if (DEBUG) {
@@ -171,7 +173,7 @@ class AssociationRequestsProcessor {
}, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
if (err == null) {
mService.createAssociationInternal(
mService.legacyCreateAssociation(
userId, deviceAddress, packageName, deviceProfile);
mServiceConnectors.forUser(userId).post(
ICompanionDeviceDiscoveryService::onAssociationCreated);
@@ -183,6 +185,18 @@ class AssociationRequestsProcessor {
}));
}
private boolean willAddRoleHolder(@NonNull AssociationRequest request,
@NonNull String packageName, @UserIdInt int userId) {
final String deviceProfile = request.getDeviceProfile();
if (deviceProfile == null) return false;
final boolean isRoleHolder = Binder.withCleanCallingIdentity(
() -> isRoleHolder(mContext, userId, packageName, deviceProfile));
// Don't need to "grant" the role, if the package already holds the role.
return !isRoleHolder;
}
private void cleanup() {
if (DEBUG) {
Slog.d(TAG, "cleanup(); discovery = "
@@ -315,4 +329,17 @@ class AssociationRequestsProcessor {
return sameOemPackageCerts;
}
private static boolean withCatchingRemoteException(ThrowingRunnable runnable) {
try {
runnable.run();
} catch (RemoteException e) {
return false;
}
return true;
}
private interface ThrowingRunnable {
void run() throws RemoteException;
}
}

View File

@@ -36,7 +36,7 @@ import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManagerCompanionDevice;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanManagerCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
@@ -411,7 +411,7 @@ public class CompanionDeviceManagerService extends SystemService {
+ "permissions to get associations for u" + userId + "/" + packageName);
}
if (!checkCallerCanManagerCompanionDevice(getContext())) {
if (!checkCallerCanManageCompanionDevice(getContext())) {
// If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to
// request the feature (also: the caller is the app itself).
checkUsesFeature(packageName, getCallingUserId());
@@ -456,7 +456,7 @@ public class CompanionDeviceManagerService extends SystemService {
if (association == null) {
throw new IllegalArgumentException("Association does not exist "
+ "or the caller does not have permissions to manage it "
+ "(ie. it belongs ot a different package or a different user).");
+ "(ie. it belongs to a different package or a different user).");
}
disassociateInternal(userId, association.getId());
@@ -594,7 +594,7 @@ public class CompanionDeviceManagerService extends SystemService {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");
createAssociationInternal(userId, macAddress, packageName, null);
legacyCreateAssociation(userId, macAddress, packageName, null);
}
private void checkCanCallNotificationApi(String callingPackage) {
@@ -671,21 +671,29 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
void createAssociationInternal(
int userId, String deviceMacAddress, String packageName, String deviceProfile) {
final AssociationInfo association = new AssociationInfo(
getNewAssociationIdForPackage(userId, packageName),
userId,
packageName,
MacAddress.fromString(deviceMacAddress),
null,
deviceProfile,
/* managedByCompanionApp */false,
/* notifyOnDeviceNearby */ false ,
System.currentTimeMillis());
/**
* @deprecated use
* {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)}
*/
@Deprecated
void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress,
@NonNull String packageName, @Nullable String deviceProfile) {
final MacAddress macAddress = MacAddress.fromString(deviceMacAddress);
createAssociation(userId, packageName, macAddress, null, deviceProfile, false);
}
AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
@Nullable String deviceProfile, boolean selfManaged) {
final int id = getNewAssociationIdForPackage(userId, packageName);
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
updateSpecialAccessPermissionForAssociatedPackage(association);
recordAssociation(association, userId);
return association;
}
@GuardedBy("mLock")

View File

@@ -49,7 +49,7 @@ class CompanionDeviceShellCommand extends android.os.ShellCommand {
int userId = getNextArgInt();
String packageName = getNextArgRequired();
String address = getNextArgRequired();
mService.createAssociationInternal(userId, address, packageName, null);
mService.legacyCreateAssociation(userId, address, packageName, null);
}
break;

View File

@@ -34,6 +34,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.os.RemoteException;
@@ -64,6 +65,19 @@ final class PermissionsUtils {
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
static void enforceCallerPermissionsToRequest(@NonNull Context context,
@NonNull AssociationRequest request, @NonNull String packageName,
@UserIdInt int userId) {
enforceCallerCanInteractWithUserId(context, userId);
enforceCallerIsSystemOr(userId, packageName);
enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile());
if (request.isSelfManaged()) {
enforceRequestSelfManagedPermission(context);
}
}
static void enforceRequestDeviceProfilePermissions(
@NonNull Context context, @Nullable String deviceProfile) {
// Device profile can be null.
@@ -139,7 +153,7 @@ final class PermissionsUtils {
}
}
static boolean checkCallerCanManagerCompanionDevice(@NonNull Context context) {
static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
if (getCallingUserId() == SYSTEM_UID) return true;
return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
@@ -158,7 +172,7 @@ final class PermissionsUtils {
if (!checkCallerCanInteractWithUserId(context, userId)) return false;
return checkCallerCanManagerCompanionDevice(context);
return checkCallerCanManageCompanionDevice(context);
}
private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) {