Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e172788 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov Committed by Android (Google) Code Review
Browse files

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

parents 22862fce d1ba3fce
Loading
Loading
Loading
Loading
+53 −26
Original line number Diff line number Diff line
@@ -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,7 +78,7 @@ 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<>(
@@ -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;
    }
}
+24 −16
Original line number Diff line number Diff line
@@ -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")
+1 −1
Original line number Diff line number Diff line
@@ -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;

+16 −2
Original line number Diff line number Diff line
@@ -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) {