summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yi-an Chen <theianchen@google.com> 2023-10-09 23:44:57 +0000
committer Yi-an Chen <theianchen@google.com> 2023-10-23 19:02:04 +0000
commitcdc0ad9ab640f68b3583cb466f07f6ad2c1cb89b (patch)
tree1930c2a07b385576e51cea1af6597abd534c45c6
parent2f340f2a8c730fd4b0a9628e560e89d5d5a4b473 (diff)
[Role Logic Move] Migrate isRoleFallbackEnabled (system server)
Add the isFallbackEnabled related fields (and setting/getting this value) in System Server. We will assume all current RolesState have version=0. If the version is undefined, it will be -1. If the isFallbackEnabled is migrated, it will be 1. What's not included in this CL: (1) Code in PC to read this value from System Server (2) Code in System Server to read from SharedPreference, migrate the current user settings, and update RolesState version Notes for the reviewer(s): (and open for discussions) (1) If the version doesn't support isFallbackEnabled (when version < 1), it will assume isFallbackEnabled is false. I think if the version < 1, we should try to migrate (in a followup CL) this user settings and update the version to be >= 1 instead of asssuming isFallbackEnabled to true. Hence there's a constructor for RolesState that sets mFallbackEnabledRoles to null. This should be used when version < 1. (2) If the roles doesn't present or the version doesn't support fallbackEnabled (see the condition in RoleUserState.setFallbackEnabled), we don't allow setting fallbackEnabled to true. Bug: 302563864 Test: Will add it after fallback logic described in Notes (2) is discussed. Change-Id: Iaeb037ae014323f0af0c43031b20f3239e359027
-rw-r--r--flags/flags.aconfig2
-rw-r--r--framework-s/Android.bp1
-rw-r--r--framework-s/api/system-current.txt2
-rw-r--r--framework-s/jarjar-rules.txt1
-rw-r--r--framework-s/java/android/app/role/IRoleManager.aidl4
-rw-r--r--framework-s/java/android/app/role/RoleManager.java49
-rw-r--r--service/Android.bp1
-rw-r--r--service/api/system-server-current.txt2
-rw-r--r--service/jarjar-rules.txt1
-rw-r--r--service/java/com/android/role/RoleService.java36
-rw-r--r--service/java/com/android/role/RoleUserState.java52
-rw-r--r--service/java/com/android/role/persistence/RolesPersistenceImpl.java12
-rw-r--r--service/java/com/android/role/persistence/RolesState.java41
-rw-r--r--service/proto/role_service.proto3
-rw-r--r--tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt50
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java12
16 files changed, 248 insertions, 21 deletions
diff --git a/flags/flags.aconfig b/flags/flags.aconfig
index 050065368..64a9647ba 100644
--- a/flags/flags.aconfig
+++ b/flags/flags.aconfig
@@ -5,4 +5,4 @@ flag {
namespace: "permissions"
description: "This flag is used to support hotword activation events in privacy dashboard"
bug: "287264308"
-} \ No newline at end of file
+}
diff --git a/framework-s/Android.bp b/framework-s/Android.bp
index e017a7ea5..076b497cb 100644
--- a/framework-s/Android.bp
+++ b/framework-s/Android.bp
@@ -69,6 +69,7 @@ java_sdk_library {
static_libs: [
"framework-permission-s-shared",
"modules-utils-build",
+ "android.permission.flags-aconfig-java",
],
apex_available: [
"com.android.permission",
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index 9545356a4..f502a3231 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -29,12 +29,14 @@ package android.app.role {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isBypassingRoleQualification();
+ method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.BYPASS_ROLE_QUALIFICATION) public void setBypassingRoleQualification(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public void setDefaultApplication(@NonNull String, @Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT";
diff --git a/framework-s/jarjar-rules.txt b/framework-s/jarjar-rules.txt
index 3b888fe99..39f2ad3b7 100644
--- a/framework-s/jarjar-rules.txt
+++ b/framework-s/jarjar-rules.txt
@@ -1,4 +1,5 @@
rule android.os.HandlerExecutor android.permission.jarjar.@0
+rule android.permission.flags.** android.permission.jarjar.@0
rule android.util.IndentingPrintWriter android.permission.jarjar.@0
rule com.android.internal.** android.permission.jarjar.@0
rule com.android.modules.** android.permission.jarjar.@0
diff --git a/framework-s/java/android/app/role/IRoleManager.aidl b/framework-s/java/android/app/role/IRoleManager.aidl
index 5f7cb1bf5..5bcda037e 100644
--- a/framework-s/java/android/app/role/IRoleManager.aidl
+++ b/framework-s/java/android/app/role/IRoleManager.aidl
@@ -54,6 +54,10 @@ interface IRoleManager {
void setBypassingRoleQualification(boolean bypassRoleQualification);
+ boolean isRoleFallbackEnabledAsUser(in String roleName, int userId);
+
+ void setRoleFallbackEnabledAsUser(in String roleName, boolean fallbackEnabled, int userId);
+
void setRoleNamesFromController(in List<String> roleNames);
boolean addRoleHolderFromController(in String roleName, in String packageName);
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index d8e7149f2..cb3ebfe3c 100644
--- a/framework-s/java/android/app/role/RoleManager.java
+++ b/framework-s/java/android/app/role/RoleManager.java
@@ -18,6 +18,7 @@ package android.app.role;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -690,6 +692,53 @@ public final class RoleManager {
}
/**
+ * Check whether role currently enables fallback to default holder.
+ * <p>
+ * This is based on the "None" holder being actively selected, in which case don't fallback.
+ *
+ * @param roleName the name of the role being queried
+ *
+ * @return whether fallback is enabled for the provided role
+ *
+ * @hide
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER)
+ @UserHandleAware
+ @SystemApi
+ public boolean isRoleFallbackEnabled(@NonNull String roleName) {
+ try {
+ return mService.isRoleFallbackEnabledAsUser(roleName,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether role should fallback to a default role holder.
+ *
+ * @param roleName the name of the role being queried.
+ * @param fallbackEnabled whether to enable fallback holders for this role.
+ *
+ * @hide
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER)
+ @UserHandleAware
+ @SystemApi
+ public void setRoleFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) {
+ try {
+ mService.setRoleFallbackEnabledAsUser(roleName, fallbackEnabled,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the names of all the available roles. Should only be called from
* {@link android.app.role.RoleControllerService}.
* <p>
diff --git a/service/Android.bp b/service/Android.bp
index 96b8fbb96..a6513508a 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -108,6 +108,7 @@ java_sdk_library {
"service-permission-shared",
"service-permission-statsd",
"service-permission-proto-stream",
+ "android.permission.flags-aconfig-java",
],
errorprone: {
javacflags: ["-Xep:GuardedBy:ERROR"],
diff --git a/service/api/system-server-current.txt b/service/api/system-server-current.txt
index b1869c2c7..ea9c9750c 100644
--- a/service/api/system-server-current.txt
+++ b/service/api/system-server-current.txt
@@ -45,6 +45,8 @@ package com.android.role.persistence {
public final class RolesState {
ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
+ ctor @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>, @NonNull java.util.Set<java.lang.String>);
+ method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @NonNull public java.util.Set<java.lang.String> getFallbackEnabledRoles();
method @Nullable public String getPackagesHash();
method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
method public int getVersion();
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 2b8765cf5..a3fd75930 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -1,4 +1,5 @@
rule android.os.HandlerExecutor com.android.permission.jarjar.@0
+rule android.permission.flags.** com.android.permission.jarjar.@0
rule android.util.IndentingPrintWriter com.android.permission.jarjar.@0
rule com.android.internal.** com.android.permission.jarjar.@0
rule com.android.modules.** com.android.permission.jarjar.@0
diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java
index f18a9e79e..6845d506b 100644
--- a/service/java/com/android/role/RoleService.java
+++ b/service/java/com/android/role/RoleService.java
@@ -649,6 +649,42 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@Override
+ public boolean isRoleFallbackEnabledAsUser(@NonNull String roleName,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "isRoleFallbackEnabledAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "isRoleFallbackEnabledAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+ return getOrCreateUserState(userId).isFallbackEnabled(roleName);
+ }
+
+ @Override
+ public void setRoleFallbackEnabledAsUser(@NonNull String roleName, boolean fallbackEnabled,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "setRoleFallbackEnabledAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "setRoleFallbackEnabledAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+ getOrCreateUserState(userId).setFallbackEnabled(roleName, fallbackEnabled);
+ }
+
+ @Override
public void setRoleNamesFromController(@NonNull List<String> roleNames) {
getContext().enforceCallingOrSelfPermission(
RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
diff --git a/service/java/com/android/role/RoleUserState.java b/service/java/com/android/role/RoleUserState.java
index 727e0f122..201633898 100644
--- a/service/java/com/android/role/RoleUserState.java
+++ b/service/java/com/android/role/RoleUserState.java
@@ -53,6 +53,8 @@ class RoleUserState {
public static final int VERSION_UNDEFINED = -1;
+ public static final int VERSION_FALLBACK_STATE_MIGRATED = 1;
+
private static final long WRITE_DELAY_MILLIS = 200;
private final RolesPersistence mPersistence = RolesPersistence.createInstance();
@@ -86,6 +88,13 @@ class RoleUserState {
@NonNull
private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
+ /**
+ *
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private ArraySet<String> mFallbackEnabledRoles = new ArraySet<>();
+
@GuardedBy("mLock")
private boolean mWriteScheduled;
@@ -193,6 +202,31 @@ class RoleUserState {
}
}
+ public boolean isFallbackEnabled(@NonNull String roleName) {
+ synchronized (mLock) {
+ return mFallbackEnabledRoles.contains(roleName);
+ }
+ }
+
+ public void setFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) {
+ synchronized (mLock) {
+ if (!mRoles.containsKey(roleName)) {
+ Log.e(LOG_TAG, "Cannot set fallback enabled for unknown role, role: " + roleName
+ + ", fallbackEnabled: " + fallbackEnabled);
+ return;
+ }
+ if (mFallbackEnabledRoles.contains(roleName) == fallbackEnabled) {
+ return;
+ }
+ if (fallbackEnabled) {
+ mFallbackEnabledRoles.add(roleName);
+ } else {
+ mFallbackEnabledRoles.remove(roleName);
+ }
+ scheduleWriteFileLocked();
+ }
+ }
+
/**
* Get whether the role is available.
*
@@ -386,7 +420,8 @@ class RoleUserState {
// Force a reconciliation on next boot if we are bypassing role qualification now.
String packagesHash = mBypassingRoleQualification ? null : mPackagesHash;
roles = new RolesState(mVersion, packagesHash,
- (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
+ (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(),
+ snapshotFallbackEnabledRoles());
}
mPersistence.writeForUser(roles, UserHandle.of(mUserId));
@@ -413,6 +448,9 @@ class RoleUserState {
if (roleState == null) {
scheduleWriteFileLocked();
+ } else {
+ mFallbackEnabledRoles.clear();
+ mFallbackEnabledRoles.addAll(roleState.getFallbackEnabledRoles());
}
}
}
@@ -427,10 +465,12 @@ class RoleUserState {
int version;
String packagesHash;
ArrayMap<String, ArraySet<String>> roles;
+ ArraySet<String> fallbackEnabledRoles;
synchronized (mLock) {
version = mVersion;
packagesHash = mPackagesHash;
roles = snapshotRolesLocked();
+ fallbackEnabledRoles = snapshotFallbackEnabledRoles();
}
long fieldToken = dumpOutputStream.start(fieldName, fieldId);
@@ -442,10 +482,12 @@ class RoleUserState {
for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
String roleName = roles.keyAt(rolesIndex);
ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
+ boolean fallbackEnabled = fallbackEnabledRoles.contains(roleName);
long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
dumpOutputStream.write("name", RoleProto.NAME, roleName);
-
+ dumpOutputStream.write("fallback_enabled", RoleProto.FALLBACK_ENABLED,
+ Boolean.toString(fallbackEnabled));
int roleHoldersSize = roleHolders.size();
for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
String roleHolder = roleHolders.valueAt(roleHoldersIndex);
@@ -485,6 +527,12 @@ class RoleUserState {
return roles;
}
+ @GuardedBy("mLock")
+ @NonNull
+ private ArraySet<String> snapshotFallbackEnabledRoles() {
+ return new ArraySet<>(mFallbackEnabledRoles);
+ }
+
/**
* Destroy this user state and delete the corresponding file. Any pending writes to the file
* will be cancelled, and any future interaction with this state will throw an exception.
diff --git a/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 76cf8f81f..242f7315f 100644
--- a/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -66,6 +66,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_FALLBACK_ENABLED = "fallbackEnabled";
private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@VisibleForTesting
@@ -142,6 +143,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
Map<String, Set<String>> roles = new ArrayMap<>();
+ Set<String> fallbackEnabledRoles = new ArraySet<>();
int type;
int depth;
int innerDepth = parser.getDepth() + 1;
@@ -153,12 +155,16 @@ public class RolesPersistenceImpl implements RolesPersistence {
if (parser.getName().equals(TAG_ROLE)) {
String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
+ String fallbackEnabled = parser.getAttributeValue(null, ATTRIBUTE_FALLBACK_ENABLED);
+ if (Boolean.parseBoolean(fallbackEnabled)) {
+ fallbackEnabledRoles.add(roleName);
+ }
Set<String> roleHolders = parseRoleHolders(parser);
roles.put(roleName, roleHolders);
}
}
- return new RolesState(version, packagesHash, roles);
+ return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);
}
@NonNull
@@ -238,12 +244,16 @@ public class RolesPersistenceImpl implements RolesPersistence {
serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
}
+ Set<String> fallbackEnabledRoles = roles.getFallbackEnabledRoles();
for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) {
String roleName = entry.getKey();
Set<String> roleHolders = entry.getValue();
+ boolean isFallbackEnabled = fallbackEnabledRoles.contains(roleName);
serializer.startTag(null, TAG_ROLE);
serializer.attribute(null, ATTRIBUTE_NAME, roleName);
+ serializer.attribute(null, ATTRIBUTE_FALLBACK_ENABLED,
+ Boolean.toString(isFallbackEnabled));
serializeRoleHolders(serializer, roleHolders);
serializer.endTag(null, TAG_ROLE);
}
diff --git a/service/java/com/android/role/persistence/RolesState.java b/service/java/com/android/role/persistence/RolesState.java
index f61efa0e8..fc6b88f26 100644
--- a/service/java/com/android/role/persistence/RolesState.java
+++ b/service/java/com/android/role/persistence/RolesState.java
@@ -16,10 +16,13 @@
package com.android.role.persistence;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
+import android.permission.flags.Flags;
+import android.util.ArraySet;
import java.util.Map;
import java.util.Objects;
@@ -33,7 +36,6 @@ import java.util.Set;
*/
@SystemApi(client = Client.SYSTEM_SERVER)
public final class RolesState {
-
/**
* The version of the roles.
*/
@@ -52,6 +54,12 @@ public final class RolesState {
private final Map<String, Set<String>> mRoles;
/**
+ * The names of roles with fallback enabled.
+ */
+ @NonNull
+ private final Set<String> mFallbackEnabledRoles;
+
+ /**
* Create a new instance of this class.
*
* @param version the version of the roles
@@ -60,9 +68,24 @@ public final class RolesState {
*/
public RolesState(int version, @Nullable String packagesHash,
@NonNull Map<String, Set<String>> roles) {
+ this(version, packagesHash, roles, new ArraySet<>());
+ }
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param version the version of the roles
+ * @param packagesHash the hash of all packages in the system
+ * @param roles the roles
+ * @param fallbackEnabledRoles the roles with fallback enabled
+ */
+ @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER)
+ public RolesState(int version, @Nullable String packagesHash,
+ @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles) {
mVersion = version;
mPackagesHash = packagesHash;
mRoles = roles;
+ mFallbackEnabledRoles = fallbackEnabledRoles;
}
/**
@@ -94,6 +117,17 @@ public final class RolesState {
return mRoles;
}
+ /**
+ * Get the fallback enabled roles.
+ *
+ * @return fallback enabled roles
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER)
+ public Set<String> getFallbackEnabledRoles() {
+ return mFallbackEnabledRoles;
+ }
+
@Override
public boolean equals(Object object) {
if (this == object) {
@@ -105,11 +139,12 @@ public final class RolesState {
RolesState that = (RolesState) object;
return mVersion == that.mVersion
&& Objects.equals(mPackagesHash, that.mPackagesHash)
- && Objects.equals(mRoles, that.mRoles);
+ && Objects.equals(mRoles, that.mRoles)
+ && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles);
}
@Override
public int hashCode() {
- return Objects.hash(mVersion, mPackagesHash, mRoles);
+ return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles);
}
}
diff --git a/service/proto/role_service.proto b/service/proto/role_service.proto
index 79c422992..f982ead5b 100644
--- a/service/proto/role_service.proto
+++ b/service/proto/role_service.proto
@@ -53,4 +53,7 @@ message RoleProto {
// The package names of the holders of this role.
repeated string holders = 2;
+
+ // Whether fallback holders are enabled for this role.
+ optional bool fallback_enabled = 3;
}
diff --git a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
index d90ffade9..0cf1fa665 100644
--- a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
@@ -20,7 +20,6 @@ import android.content.ApexEnvironment
import android.content.Context
import android.os.Process
import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.google.common.truth.Truth.assertThat
@@ -29,6 +28,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
@@ -37,27 +37,36 @@ import org.mockito.MockitoAnnotations.initMocks
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
class RolesPersistenceTest {
private val context = InstrumentationRegistry.getInstrumentation().context
private lateinit var mockDataDirectory: File
-
private lateinit var mockitoSession: MockitoSession
@Mock lateinit var apexEnvironment: ApexEnvironment
+ @Parameterized.Parameter(0) lateinit var stateVersion: StateVersion
+ private lateinit var state: RolesState
private val persistence = RolesPersistenceImpl {}
- private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2")))
+ private val defaultRoles = mapOf(ROLE_NAME to setOf(HOLDER_1, HOLDER_2))
+ private val stateVersionUndefined = RolesState(VERSION_UNDEFINED, PACKAGE_HASH, defaultRoles)
+ private val stateVersionFallbackMigrated =
+ RolesState(VERSION_FALLBACK_MIGRATED, PACKAGE_HASH, defaultRoles, setOf(ROLE_NAME))
private val user = Process.myUserHandle()
@Before
- fun createMockDataDirectory() {
+ fun setUp() {
+ createMockDataDirectory()
+ mockApexEnvironment()
+ state = getState()
+ }
+
+ private fun createMockDataDirectory() {
mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
}
- @Before
- fun mockApexEnvironment() {
+ private fun mockApexEnvironment() {
initMocks(this)
mockitoSession =
mockitoSession()
@@ -80,7 +89,7 @@ class RolesPersistenceTest {
persistence.writeForUser(state, user)
val persistedState = persistence.readForUser(user)
- checkPersistedState(persistedState)
+ assertThat(persistedState).isEqualTo(state)
}
@Test
@@ -91,7 +100,7 @@ class RolesPersistenceTest {
.writeText("<roles version=\"-1\"><role name=\"com.foo.bar\"><holder")
val persistedState = persistence.readForUser(user)
- checkPersistedState(persistedState)
+ assertThat(persistedState).isEqualTo(state)
}
@Test
@@ -102,15 +111,28 @@ class RolesPersistenceTest {
assertThat(persistedState).isNull()
}
+ private fun getState(): RolesState =
+ when (stateVersion) {
+ StateVersion.VERSION_UNDEFINED -> stateVersionUndefined
+ StateVersion.VERSION_FALLBACK_MIGRATED -> stateVersionFallbackMigrated
+ }
- private fun checkPersistedState(persistedState: RolesState?) {
- assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState?.version).isEqualTo(state.version)
- assertThat(persistedState?.packagesHash).isEqualTo(state.packagesHash)
- assertThat(persistedState?.roles).isEqualTo(state.roles)
+ enum class StateVersion {
+ VERSION_UNDEFINED,
+ VERSION_FALLBACK_MIGRATED
}
companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): Array<StateVersion> = StateVersion.values()
+
+ private const val VERSION_UNDEFINED = -1
+ private const val VERSION_FALLBACK_MIGRATED = 1
private const val APEX_MODULE_NAME = "com.android.permission"
+ private const val PACKAGE_HASH = "packagesHash"
+ private const val ROLE_NAME = "roleName"
+ private const val HOLDER_1 = "holder1"
+ private const val HOLDER_2 = "holder2"
}
}
diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
index a7429ac99..61625d587 100644
--- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
@@ -1153,6 +1153,18 @@ public class RoleManagerTest {
});
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ codeName = "VanillaIceCream")
+ @Test
+ public void testSetAndGetRoleFallbackEnabled() {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setRoleFallbackEnabled(RoleManager.ROLE_SMS, true);
+ assertThat(sRoleManager.isRoleFallbackEnabled(RoleManager.ROLE_SMS)).isTrue();
+ });
+ }
+
@NonNull
private List<String> getRoleHolders(@NonNull String roleName) throws Exception {
return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName));