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

Commit 95377858 authored by Raphael Kim's avatar Raphael Kim
Browse files

[W][CDM] Add wearable_sensing device profile which grants the app access to a...

[W][CDM] Add wearable_sensing device profile which grants the app access to a specific transport spec

Bug: 377767141
Flag: EXEMPT bugfix
Test: atest CtsCompanionDeviceManagerCoreTestCases:DeviceProfilesTest
Change-Id: I425e042d52e7363cc145de7ed55b49f1686d83a3
parent 298ddd06
Loading
Loading
Loading
Loading
+17 −2
Original line number Diff line number Diff line
@@ -90,7 +90,21 @@ public final class AssociationRequest implements Parcelable {
    public static final String DEVICE_PROFILE_GLASSES = "android.app.role.COMPANION_DEVICE_GLASSES";

    /**
     * Device profile: a virtual device capable of rendering Android applications, and sending back
     * Device profile: a wearable device capable of sensing its surroundings.
     * <p>
     * This device profile is not tied to any android role, and is used to identify the device
     * as a wearable sensing device.
     * <p>
     * This profile may only be used by the system.
     *
     * @see AssociationRequest.Builder#setDeviceProfile
     * @hide
     */
    public static final String DEVICE_PROFILE_WEARABLE_SENSING =
            "android.companion.COMPANION_DEVICE_WEARABLE_SENSING";

    /**
     * Device profile: a virtual display capable of rendering Android applications, and sending back
     * input events.
     * <p>
     * Only applications that have been granted
@@ -163,7 +177,8 @@ public final class AssociationRequest implements Parcelable {
    @Retention(RetentionPolicy.SOURCE)
    @StringDef(value = { DEVICE_PROFILE_WATCH, DEVICE_PROFILE_COMPUTER,
            DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, DEVICE_PROFILE_APP_STREAMING,
            DEVICE_PROFILE_GLASSES, DEVICE_PROFILE_NEARBY_DEVICE_STREAMING })
            DEVICE_PROFILE_GLASSES, DEVICE_PROFILE_NEARBY_DEVICE_STREAMING,
            DEVICE_PROFILE_WEARABLE_SENSING })
    public @interface DeviceProfile {}

    /**
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;

import static java.util.Collections.unmodifiableMap;
@@ -210,6 +211,7 @@ final class CompanionDeviceResources {
        set.add(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION);
        set.add(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
        set.add(DEVICE_PROFILE_SENSOR_DEVICE_STREAMING);
        set.add(DEVICE_PROFILE_WEARABLE_SENSING);
        set.add(null);

        SUPPORTED_SELF_MANAGED_PROFILES = unmodifiableSet(set);
+15 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static com.android.server.companion.utils.PackageUtils.enforceUsesCompani
import static com.android.server.companion.utils.PermissionsUtils.enforcePermissionForCreatingAssociation;
import static com.android.server.companion.utils.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
import static com.android.server.companion.utils.RolesUtils.isRolelessProfile;
import static com.android.server.companion.utils.Utils.prepareForIpc;

import static java.util.Objects.requireNonNull;
@@ -313,12 +314,20 @@ public class AssociationRequestsProcessor {
    public void maybeGrantRoleAndStoreAssociation(@NonNull AssociationInfo association,
            @Nullable IAssociationRequestCallback callback,
            @Nullable ResultReceiver resultReceiver) {
        // If the "Device Profile" is specified, make the companion application a holder of the
        // corresponding role.
        // If it is null, then the operation will succeed without granting any role.
        final String deviceProfile = association.getDeviceProfile();

        // If device profile is not specified or role-less, skip role grant and store association.
        if (deviceProfile == null || isRolelessProfile(deviceProfile)) {
            mAssociationStore.addAssociation(association);
            sendCallbackAndFinish(association, callback, resultReceiver);
            return;
        }

        // If the "Device Profile" is specified and it is associated with a role, then make the
        // companion application a holder of the corresponding role.
        addRoleHolderForAssociation(mContext, association, success -> {
            if (success) {
                Slog.i(TAG, "Added " + association.getDeviceProfile() + " role to userId="
                Slog.i(TAG, "Added " + deviceProfile + " role to userId="
                        + association.getUserId() + ", packageName="
                        + association.getPackageName());
                mAssociationStore.addAssociation(association);
@@ -326,7 +335,7 @@ public class AssociationRequestsProcessor {
            } else {
                Slog.e(TAG, "Failed to add u" + association.getUserId()
                        + "\\" + association.getPackageName()
                        + " to the list of " + association.getDeviceProfile() + " holders.");
                        + " to the list of " + deviceProfile + " holders.");
                sendCallbackAndFinish(null, callback, resultReceiver);
            }
        });
@@ -406,7 +415,7 @@ public class AssociationRequestsProcessor {
    private boolean willAddRoleHolder(@NonNull AssociationRequest request,
            @NonNull String packageName, @UserIdInt int userId) {
        final String deviceProfile = request.getDeviceProfile();
        if (deviceProfile == null) return false;
        if (deviceProfile == null || isRolelessProfile(deviceProfile)) return false;

        final boolean isRoleHolder = Binder.withCleanCallingIdentity(
                () -> isRoleHolder(mContext, userId, packageName, deviceProfile));
+6 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;

import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__ACTION__CREATED;
@@ -35,6 +36,7 @@ import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_NULL;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WATCH;
import static com.android.internal.util.FrameworkStatsLog.CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WEARABLE_SENSING;
import static com.android.internal.util.FrameworkStatsLog.write;

import static java.util.Collections.unmodifiableMap;
@@ -77,6 +79,10 @@ public final class MetricUtils {
                DEVICE_PROFILE_SENSOR_DEVICE_STREAMING,
                CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_SENSOR_DEVICE_STREAMING
        );
        map.put(
                DEVICE_PROFILE_WEARABLE_SENSING,
                CDM_ASSOCIATION_ACTION__DEVICE_PROFILE__DEVICE_PROFILE_WEARABLE_SENSING
        );

        METRIC_DEVICE_PROFILE = unmodifiableMap(map);
    }
+20 −3
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES;
import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_SENSOR_DEVICE_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Binder.getCallingPid;
import static android.os.Binder.getCallingUid;
@@ -39,6 +40,7 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.server.companion.utils.RolesUtils.isRoleHolder;

import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;

import android.Manifest;
import android.annotation.NonNull;
@@ -51,10 +53,12 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.app.IAppOpsService;

import java.util.Map;
import java.util.Set;

/**
 * Utility methods for checking permissions required for accessing {@link CompanionDeviceManager}
@@ -64,6 +68,13 @@ import java.util.Map;
 */
public final class PermissionsUtils {

    private static final Set<String> SYSTEM_ONLY_DEVICE_PROFILES;
    static {
        final Set<String> set = new ArraySet<>();
        set.add(DEVICE_PROFILE_WEARABLE_SENSING);
        SYSTEM_ONLY_DEVICE_PROFILES = unmodifiableSet(set);
    }

    private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
    static {
        final Map<String, String> map = new ArrayMap<>();
@@ -102,12 +113,18 @@ public final class PermissionsUtils {
        // Device profile can be null.
        if (deviceProfile == null) return;

        if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
        if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)
                && !SYSTEM_ONLY_DEVICE_PROFILES.contains(deviceProfile)) {
            throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
        }

        final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
        if (context.checkPermission(permission, getCallingPid(), packageUid)
        if (SYSTEM_ONLY_DEVICE_PROFILES.contains(deviceProfile) && getCallingUid() != SYSTEM_UID) {
            throw new SecurityException("Caller must be system to associate with a device with "
                    + deviceProfile + " profile.");
        }

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