summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Raphael Kim <raphk@google.com> 2024-10-30 14:51:51 -0700
committer Raphael Kim <raphk@google.com> 2025-01-27 23:57:14 -0800
commit95377858760b57731510c44e91c681c942b5e099 (patch)
treee1a68047beaa7daf932c2086d83fb2a12909ac7d
parent298ddd06aecc47120690b95aaa14d8ea9e20856d (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
-rw-r--r--core/java/android/companion/AssociationRequest.java19
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java2
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java21
-rw-r--r--services/companion/java/com/android/server/companion/utils/MetricUtils.java6
-rw-r--r--services/companion/java/com/android/server/companion/utils/PermissionsUtils.java23
-rw-r--r--services/companion/java/com/android/server/companion/utils/RolesUtils.java18
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() {}
}