Merge "Implement "no-UI" flow in CDM"
This commit is contained in:
committed by
Android (Google) Code Review
commit
1e172788b0
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user