summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Chien <markchien@google.com>2021-05-18 03:37:13 +0000
committerMark Chien <markchien@google.com>2021-05-18 09:44:04 +0000
commit51e93fbf8952bf708c71cecc29b8c867336f68b6 (patch)
tree6f4ad7a58d05634a39ebe206e9f45bbd68c92c5e
parent8d7885b5546bcd038670ad29575ead327756e118 (diff)
downloadnet-51e93fbf8952bf708c71cecc29b8c867336f68b6.tar.gz
Add isFeatureEnabled by checking with apex module version
A variable of #isFeatureEnabled which check apex module version instead of apk version. Bug: 187946226 Test: NetworkStaticLibTests Original change: https://android-review.googlesource.com/c/platform/frameworks/libs/net/+/1705174 Ignore-AOSP-First: manual cherry-pick from AOSP build automerge is broken Change-Id: I68659e43d7fdf40678ceb9df2ff10d480a5d08e0
-rw-r--r--common/device/com/android/net/module/util/DeviceConfigUtils.java83
-rw-r--r--common/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java52
2 files changed, 128 insertions, 7 deletions
diff --git a/common/device/com/android/net/module/util/DeviceConfigUtils.java b/common/device/com/android/net/module/util/DeviceConfigUtils.java
index 5d03dfd2..77b7835b 100644
--- a/common/device/com/android/net/module/util/DeviceConfigUtils.java
+++ b/common/device/com/android/net/module/util/DeviceConfigUtils.java
@@ -17,6 +17,7 @@
package com.android.net.module.util;
import android.content.Context;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.provider.DeviceConfig;
@@ -38,6 +39,7 @@ public final class DeviceConfigUtils {
@VisibleForTesting
public static void resetPackageVersionCacheForTest() {
sPackageVersion = -1;
+ sModuleVersion = -1;
}
private static volatile long sPackageVersion = -1;
@@ -155,11 +157,8 @@ public final class DeviceConfigUtils {
public static boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
@NonNull String name, boolean defaultEnabled) {
try {
- final int propertyVersion = getDeviceConfigPropertyInt(namespace, name,
- 0 /* default value */);
final long packageVersion = getPackageVersion(context);
- return (propertyVersion == 0 && defaultEnabled)
- || (propertyVersion != 0 && packageVersion >= (long) propertyVersion);
+ return isFeatureEnabled(context, packageVersion, namespace, name, defaultEnabled);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Could not find the package name", e);
return false;
@@ -167,6 +166,82 @@ public final class DeviceConfigUtils {
}
/**
+ * Check whether or not one specific experimental feature for a particular namespace from
+ * {@link DeviceConfig} is enabled by comparing module package version
+ * with current version of property. If this property version is valid, the corresponding
+ * experimental feature would be enabled, otherwise disabled.
+ *
+ * This is useful to ensure that if a module install is rolled back, flags are not left fully
+ * rolled out on a version where they have not been well tested.
+ * @param context The global context information about an app environment.
+ * @param namespace The namespace containing the property to look up.
+ * @param name The name of the property to look up.
+ * @param moduleName The mainline module name which is released as apex.
+ * @param defaultEnabled The value to return if the property does not exist or its value is
+ * null.
+ * @return true if this feature is enabled, or false if disabled.
+ */
+ public static boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, @NonNull String moduleName, boolean defaultEnabled) {
+ try {
+ final long packageVersion = getModuleVersion(context, moduleName);
+ return isFeatureEnabled(context, packageVersion, namespace, name, defaultEnabled);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not find the module name", e);
+ return false;
+ }
+ }
+
+ private static boolean maybeUseFixedPackageVersion(@NonNull Context context) {
+ final String packageName = context.getPackageName();
+ if (packageName == null) return false;
+
+ return packageName.equals("com.android.networkstack.tethering")
+ || packageName.equals("com.android.networkstack.tethering.inprocess");
+ }
+
+ private static boolean isFeatureEnabled(@NonNull Context context, long packageVersion,
+ @NonNull String namespace, String name, boolean defaultEnabled)
+ throws PackageManager.NameNotFoundException {
+ final int propertyVersion = getDeviceConfigPropertyInt(namespace, name,
+ 0 /* default value */);
+ return (propertyVersion == 0 && defaultEnabled)
+ || (propertyVersion != 0 && packageVersion >= (long) propertyVersion);
+ }
+
+ private static volatile long sModuleVersion = -1;
+ @VisibleForTesting public static long FIXED_PACKAGE_VERSION = 10;
+ private static long getModuleVersion(@NonNull Context context, @NonNull String moduleName)
+ throws PackageManager.NameNotFoundException {
+ if (sModuleVersion >= 0) return sModuleVersion;
+
+ final PackageManager packageManager = context.getPackageManager();
+ ModuleInfo module;
+ try {
+ module = packageManager.getModuleInfo(
+ moduleName, PackageManager.MODULE_APEX_NAME);
+ } catch (PackageManager.NameNotFoundException e) {
+ // The error may happen if mainline module meta data is not installed e.g. there are
+ // no meta data configuration in AOSP build. To be able to enable a feature in AOSP
+ // by setting a flag via ADB for example. set a small non-zero fixed number for
+ // comparing.
+ if (maybeUseFixedPackageVersion(context)) {
+ sModuleVersion = FIXED_PACKAGE_VERSION;
+ return FIXED_PACKAGE_VERSION;
+ } else {
+ throw e;
+ }
+ }
+ String modulePackageName = module.getPackageName();
+ if (modulePackageName == null) throw new PackageManager.NameNotFoundException(moduleName);
+ final long version = packageManager.getPackageInfo(modulePackageName,
+ PackageManager.MATCH_APEX).getLongVersionCode();
+ sModuleVersion = version;
+
+ return version;
+ }
+
+ /**
* Gets boolean config from resources.
*/
public static boolean getResBooleanConfig(@NonNull final Context context,
diff --git a/common/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java b/common/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
index 57316b2f..c65f7934 100644
--- a/common/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
+++ b/common/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
@@ -18,6 +18,7 @@ package com.android.net.module.util;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.net.module.util.DeviceConfigUtils.FIXED_PACKAGE_VERSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -30,6 +31,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -64,10 +66,13 @@ public class DeviceConfigUtilsTest {
private static final int TEST_MIN_FLAG_VALUE = 100;
private static final long TEST_PACKAGE_VERSION = 290000000;
private static final String TEST_PACKAGE_NAME = "test.package.name";
+ private static final String TETHERING_AOSP_PACKAGE_NAME = "com.android.networkstack.tethering";
+ private static final String TEST_APEX_NAME = "test.apex.name";
private MockitoSession mSession;
@Mock private Context mContext;
@Mock private PackageManager mPm;
+ @Mock private ModuleInfo mMi;
@Mock private PackageInfo mPi;
@Mock private Resources mResources;
@@ -81,6 +86,8 @@ public class DeviceConfigUtilsTest {
doReturn(mPm).when(mContext).getPackageManager();
doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
+ doReturn(mMi).when(mPm).getModuleInfo(eq(TEST_APEX_NAME), anyInt());
+ doReturn(TEST_PACKAGE_NAME).when(mMi).getPackageName();
doReturn(pi).when(mPm).getPackageInfo(anyString(), anyInt());
doReturn(mResources).when(mContext).getResources();
}
@@ -193,23 +200,54 @@ public class DeviceConfigUtilsTest {
eq(TEST_EXPERIMENT_FLAG)));
assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
TEST_EXPERIMENT_FLAG));
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
}
@Test
- public void testFeatureIsNotEnabled() {
+ public void testFeatureDefaultEnabled() {
doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
TEST_EXPERIMENT_FLAG));
+ assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, true /* defaultEnabled */));
}
@Test
public void testFeatureIsEnabledWithException() throws Exception {
- doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
- eq(TEST_EXPERIMENT_FLAG)));
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt());
assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
TEST_EXPERIMENT_FLAG));
+ assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+ doThrow(NameNotFoundException.class).when(mPm).getModuleInfo(anyString(), anyInt());
+ assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+ }
+
+
+ @Test
+ public void testFeatureIsEnabledUsingFixedVersion() throws Exception {
+ doReturn(TETHERING_AOSP_PACKAGE_NAME).when(mContext).getPackageName();
+ doThrow(NameNotFoundException.class).when(mPm).getModuleInfo(anyString(), anyInt());
+
+ doReturn(Long.toString(FIXED_PACKAGE_VERSION)).when(() -> DeviceConfig.getProperty(
+ eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+
+ doReturn(Long.toString(FIXED_PACKAGE_VERSION + 1)).when(() -> DeviceConfig.getProperty(
+ eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
+ assertFalse(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+
+ doReturn(Long.toString(FIXED_PACKAGE_VERSION - 1)).when(() -> DeviceConfig.getProperty(
+ eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
}
@Test
@@ -225,6 +263,14 @@ public class DeviceConfigUtilsTest {
verify(mContext, times(1)).getPackageManager();
verify(mContext, times(1)).getPackageName();
verify(mPm, times(1)).getPackageInfo(anyString(), anyInt());
+
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+ assertTrue(DeviceConfigUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE,
+ TEST_EXPERIMENT_FLAG, TEST_APEX_NAME, false /* defaultEnabled */));
+
+ // Module info is only queried once
+ verify(mPm, times(1)).getModuleInfo(anyString(), anyInt());
}
@Test