diff options
author | 2024-10-30 14:51:51 -0700 | |
---|---|---|
committer | 2025-01-27 23:57:14 -0800 | |
commit | 95377858760b57731510c44e91c681c942b5e099 (patch) | |
tree | e1a68047beaa7daf932c2086d83fb2a12909ac7d | |
parent | 298ddd06aecc47120690b95aaa14d8ea9e20856d (diff) |
[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
6 files changed, 78 insertions, 11 deletions
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 32cbf326c923..b658452add4f 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -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 {} /** diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java index fd771640ec09..f756a6235c14 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java @@ -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); diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java index 899b302316f9..1095ac31e120 100644 --- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java @@ -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)); diff --git a/services/companion/java/com/android/server/companion/utils/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java index 83cbde6639c0..cfa7cb00dfac 100644 --- a/services/companion/java/com/android/server/companion/utils/MetricUtils.java +++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java @@ -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); } diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java index 6431af5b21ac..f4128b820d8f 100644 --- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java @@ -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."); diff --git a/services/companion/java/com/android/server/companion/utils/RolesUtils.java b/services/companion/java/com/android/server/companion/utils/RolesUtils.java index dd12e0406089..2b281f3f342e 100644 --- a/services/companion/java/com/android/server/companion/utils/RolesUtils.java +++ b/services/companion/java/com/android/server/companion/utils/RolesUtils.java @@ -23,12 +23,16 @@ import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.role.RoleManager; import android.companion.AssociationInfo; +import android.companion.AssociationRequest; import android.content.Context; import android.os.Binder; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Slog; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.function.Consumer; /** Utility methods for accessing {@link RoleManager} APIs. */ @@ -37,6 +41,13 @@ public final class RolesUtils { private static final String TAG = "CDM_RolesUtils"; + private static final Set<String> ROLELESS_DEVICE_PROFILES; + static { + final Set<String> profiles = new ArraySet<>(); + profiles.add(AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING); + ROLELESS_DEVICE_PROFILES = Collections.unmodifiableSet(profiles); + } + /** * Check if the package holds the role. */ @@ -107,5 +118,12 @@ public final class RolesUtils { ); } + /** + * Return true if the device profile is not tied to an Android role. + */ + public static boolean isRolelessProfile(String deviceProfile) { + return ROLELESS_DEVICE_PROFILES.contains(deviceProfile); + } + private RolesUtils() {} } |