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

Commit d7eb72b9 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov
Browse files

Permissions checks for CDM.associate()

Bug: 209581046
Bug: 208307075
Test: atest CompanionDeviceManagerTest
Change-Id: Ieb3ae096ed6e87cabf5a86db4cf2b92308e50426
parent f7d3ea87
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -20,7 +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.enforceCallerPermissionsToRequest;
import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
import static com.android.server.companion.RolesUtils.isRoleHolder;

import static java.util.Objects.requireNonNull;
@@ -110,7 +110,7 @@ class AssociationRequestsProcessor {
        }

        // 1. Enforce permissions and other requirements.
        enforceCallerPermissionsToRequest(mContext, request, packageName, userId);
        enforcePermissionsForAssociation(mContext, request, packageName, userId);
        mService.checkUsesFeature(packageName, userId);

        // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
+10 −8
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainRunna
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
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.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
@@ -401,15 +402,16 @@ public class CompanionDeviceManagerService extends SystemService {
            Slog.i(LOG_TAG, "associate() "
                    + "request=" + request + ", "
                    + "package=u" + userId + "/" + packageName);
            enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
                    "create associations");

            mAssociationRequestsProcessor.process(request, packageName, userId, callback);
        }

        @Override
        public List<AssociationInfo> getAssociations(String packageName, int userId) {
            if (!checkCallerCanManageAssociationsForPackage(getContext(), userId, packageName)) {
                throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
                        + "permissions to get associations for u" + userId + "/" + packageName);
            }
            enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
                    "get associations");

            if (!checkCallerCanManageCompanionDevice(getContext())) {
                // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to
@@ -424,7 +426,7 @@ public class CompanionDeviceManagerService extends SystemService {
        @Override
        public List<AssociationInfo> getAllAssociationsForUser(int userId) throws RemoteException {
            enforceCallerCanInteractWithUserId(getContext(), userId);
            enforceCallerCanManagerCompanionDevice(getContext(), "getAllAssociationsForUser");
            enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser");

            return new ArrayList<>(
                    CompanionDeviceManagerService.this.getAllAssociationsForUser(userId));
@@ -434,7 +436,7 @@ public class CompanionDeviceManagerService extends SystemService {
        public void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
                int userId) {
            enforceCallerCanInteractWithUserId(getContext(), userId);
            enforceCallerCanManagerCompanionDevice(getContext(),
            enforceCallerCanManageCompanionDevice(getContext(),
                    "addOnAssociationsChangedListener");

            //TODO: Implement.
@@ -621,7 +623,7 @@ public class CompanionDeviceManagerService extends SystemService {
        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                throws RemoteException {
            enforceCallerCanManagerCompanionDevice(getContext(), "onShellCommand");
            enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
            new CompanionDeviceShellCommand(CompanionDeviceManagerService.this)
                    .exec(this, in, out, err, args, callback, resultReceiver);
        }
+39 −11
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Binder.getCallingPid;
import static android.os.Binder.getCallingUid;
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.getCallingUserId;
@@ -37,11 +38,13 @@ import android.annotation.UserIdInt;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;

import com.android.internal.app.IAppOpsService;
import com.android.server.LocalServices;

import java.util.Map;

@@ -65,21 +68,19 @@ final class PermissionsUtils {
        DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
    }

    static void enforceCallerPermissionsToRequest(@NonNull Context context,
    static void enforcePermissionsForAssociation(@NonNull Context context,
            @NonNull AssociationRequest request, @NonNull String packageName,
            @UserIdInt int userId) {
        enforceCallerCanInteractWithUserId(context, userId);
        enforceCallerIsSystemOr(userId, packageName);

        enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile());
        final int packageUid = getPackageUid(userId, packageName);
        enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid);

        if (request.isSelfManaged()) {
            enforceRequestSelfManagedPermission(context);
            enforceRequestSelfManagedPermission(context, packageUid);
        }
    }

    static void enforceRequestDeviceProfilePermissions(
            @NonNull Context context, @Nullable String deviceProfile) {
            @NonNull Context context, @Nullable String deviceProfile, int packageUid) {
        // Device profile can be null.
        if (deviceProfile == null) return;

@@ -100,14 +101,15 @@ final class PermissionsUtils {
        }

        final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
        if (context.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
        if (context.checkPermission(permission, getCallingPid(), packageUid)
                != PERMISSION_GRANTED) {
            throw new SecurityException("Application must hold " + permission + " to associate "
                    + "with a device with " + deviceProfile + " profile.");
        }
    }

    static void enforceRequestSelfManagedPermission(@NonNull Context context) {
        if (context.checkCallingOrSelfPermission(REQUEST_COMPANION_SELF_MANAGED)
    static void enforceRequestSelfManagedPermission(@NonNull Context context, int packageUid) {
        if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid)
                != PERMISSION_GRANTED) {
            throw new SecurityException("Application does not hold "
                    + REQUEST_COMPANION_SELF_MANAGED);
@@ -159,13 +161,34 @@ final class PermissionsUtils {
        return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
    }

    static void enforceCallerCanManagerCompanionDevice(@NonNull Context context,
    static void enforceCallerCanManageCompanionDevice(@NonNull Context context,
            @Nullable String message) {
        if (getCallingUid() == SYSTEM_UID) return;

        context.enforceCallingPermission(MANAGE_COMPANION_DEVICES, message);
    }

    static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
            @UserIdInt int userId, @NonNull String packageName,
            @Nullable String actionDescription) {
        if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;

        throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
                + "permissions to "
                + (actionDescription != null ? actionDescription : "manage associations")
                + " for u" + userId + "/" + packageName);
    }

    /**
     * Check if the caller is either:
     * <ul>
     * <li> the package itself
     * <li> the System ({@link android.os.Process#SYSTEM_UID})
     * <li> holds {@link Manifest.permission#MANAGE_COMPANION_DEVICES} and, if belongs to a
     * different user, also holds {@link Manifest.permission#INTERACT_ACROSS_USERS}.
     * </ul>
     * @return whether the caller is one of the above.
     */
    static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
            @UserIdInt int userId, @NonNull String packageName) {
        if (checkCallerIsSystemOr(userId, packageName)) return true;
@@ -184,6 +207,11 @@ final class PermissionsUtils {
        }
    }

    private static int getPackageUid(@UserIdInt int userId, @NonNull String packageName) {
        return LocalServices.getService(PackageManagerInternal.class)
                .getPackageUid(packageName, 0, userId);
    }

    private static IAppOpsService getAppOpsService() {
        if (sAppOpsService == null) {
            synchronized (PermissionsUtils.class) {