Support checking DnsResolver version in isFeatureSupported.

Test: new unit tests
Change-Id: Ia251c068806a3d8c970d984322d4cb7e2364c1ef
This commit is contained in:
Lorenzo Colitti 2024-08-29 19:32:12 +09:00
commit ca67820a94
3 changed files with 89 additions and 20 deletions

View file

@ -22,6 +22,7 @@ import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.net.module.util.FeatureVersions.CONNECTIVITY_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.DNS_RESOLVER_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.MODULE_MASK;
import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.VERSION_MASK;
@ -68,7 +69,8 @@ public final class DeviceConfigUtils {
@VisibleForTesting
public static void resetPackageVersionCacheForTest() {
sPackageVersion = -1;
sModuleVersion = -1;
sTetheringModuleVersion = -1;
sResolvModuleVersion = -1;
sNetworkStackModuleVersion = -1;
}
@ -243,23 +245,23 @@ public final class DeviceConfigUtils {
}
}
// Guess the tethering module name based on the package prefix of the connectivity resources
// Take the resource package name, cut it before "connectivity" and append "tethering".
// Guess an APEX module name based on the package prefix of the connectivity resources
// Take the resource package name, cut it before "connectivity" and append the module name.
// Then resolve that package version number with packageManager.
// If that fails retry by appending "go.tethering" instead
private static long resolveTetheringModuleVersion(@NonNull Context context)
// If that fails retry by appending "go.<moduleName>" instead.
private static long resolveApexModuleVersion(@NonNull Context context, String moduleName)
throws PackageManager.NameNotFoundException {
final String pkgPrefix = resolvePkgPrefix(context);
final PackageManager packageManager = context.getPackageManager();
try {
return packageManager.getPackageInfo(pkgPrefix + "tethering",
return packageManager.getPackageInfo(pkgPrefix + moduleName,
PackageManager.MATCH_APEX).getLongVersionCode();
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Device is using go modules");
// fall through
}
return packageManager.getPackageInfo(pkgPrefix + "go.tethering",
return packageManager.getPackageInfo(pkgPrefix + "go." + moduleName,
PackageManager.MATCH_APEX).getLongVersionCode();
}
@ -274,19 +276,35 @@ public final class DeviceConfigUtils {
return connResourcesPackage.substring(0, pkgPrefixLen);
}
private static volatile long sModuleVersion = -1;
private static volatile long sTetheringModuleVersion = -1;
private static long getTetheringModuleVersion(@NonNull Context context) {
if (sModuleVersion >= 0) return sModuleVersion;
if (sTetheringModuleVersion >= 0) return sTetheringModuleVersion;
try {
sModuleVersion = resolveTetheringModuleVersion(context);
sTetheringModuleVersion = resolveApexModuleVersion(context, "tethering");
} catch (PackageManager.NameNotFoundException e) {
// It's expected to fail tethering module version resolution on the devices with
// flattened apex
Log.e(TAG, "Failed to resolve tethering module version: " + e);
return DEFAULT_PACKAGE_VERSION;
}
return sModuleVersion;
return sTetheringModuleVersion;
}
private static volatile long sResolvModuleVersion = -1;
private static long getResolvModuleVersion(@NonNull Context context) {
if (sResolvModuleVersion >= 0) return sResolvModuleVersion;
try {
sResolvModuleVersion = resolveApexModuleVersion(context, "resolv");
} catch (PackageManager.NameNotFoundException e) {
// It's expected to fail resolv module version resolution on the devices with
// flattened apex
Log.e(TAG, "Failed to resolve resolv module version: " + e);
return DEFAULT_PACKAGE_VERSION;
}
return sResolvModuleVersion;
}
private static volatile long sNetworkStackModuleVersion = -1;
@ -342,6 +360,8 @@ public final class DeviceConfigUtils {
moduleVersion = getTetheringModuleVersion(context);
} else if (moduleId == NETWORK_STACK_MODULE_ID) {
moduleVersion = getNetworkStackModuleVersion(context);
} else if (moduleId == DNS_RESOLVER_MODULE_ID) {
moduleVersion = getResolvModuleVersion(context);
} else {
throw new IllegalArgumentException("Unknown module " + moduleId);
}

View file

@ -37,6 +37,7 @@ public class FeatureVersions {
public static final long VERSION_MASK = 0x00F_FFFF_FFFFL;
public static final long CONNECTIVITY_MODULE_ID = 0x01L << MODULE_SHIFT;
public static final long NETWORK_STACK_MODULE_ID = 0x02L << MODULE_SHIFT;
public static final long DNS_RESOLVER_MODULE_ID = 0x03L << MODULE_SHIFT;
// CLAT_ADDRESS_TRANSLATE is a feature of the network stack, which doesn't throw when system
// try to add a NAT-T keepalive packet filter with v6 address, introduced in version
// M-2023-Sept on July 3rd, 2023.
@ -48,4 +49,11 @@ public class FeatureVersions {
// by BPF for the given uid and conditions, introduced in version M-2024-Feb on Nov 6, 2023.
public static final long FEATURE_IS_UID_NETWORKING_BLOCKED =
CONNECTIVITY_MODULE_ID + 34_14_00_000L;
// DDR is a feature implemented across NetworkStack, ConnectivityService and DnsResolver.
// The flag that enables this feature is in NetworkStack.
public static final long FEATURE_DDR_IN_CONNECTIVITY =
CONNECTIVITY_MODULE_ID + 35_11_00_000L;
public static final long FEATURE_DDR_IN_DNSRESOLVER =
DNS_RESOLVER_MODULE_ID + 35_11_00_000L;
}

View file

@ -24,6 +24,7 @@ import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
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.FeatureVersions.CONNECTIVITY_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.DNS_RESOLVER_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID;
import static org.junit.Assert.assertEquals;
@ -77,7 +78,9 @@ public class DeviceConfigUtilsTest {
private static final int TEST_DEFAULT_FLAG_VALUE = 0;
private static final int TEST_MAX_FLAG_VALUE = 1000;
private static final int TEST_MIN_FLAG_VALUE = 100;
private static final long TEST_PACKAGE_VERSION = 290000000;
private static final long TEST_PACKAGE_VERSION = 290500000;
private static final long TEST_GO_PACKAGE_VERSION = 290000000; // Not updated
private static final long TEST_RESOLV_PACKAGE_VERSION = 290300000; // Updated, but older.
private static final String TEST_PACKAGE_NAME = "test.package.name";
// The APEX name is the name of the APEX module, as in android.content.pm.ModuleInfo, and is
// used for its mount point in /apex. APEX packages are actually APKs with a different
@ -85,14 +88,18 @@ public class DeviceConfigUtilsTest {
// that manifest, and is reflected in android.content.pm.ApplicationInfo. Contrary to the APEX
// (module) name, different package names are typically used to identify the organization that
// built and signed the APEX modules.
private static final String TEST_APEX_PACKAGE_NAME = "com.prefix.android.tethering";
private static final String TEST_GO_APEX_PACKAGE_NAME = "com.prefix.android.go.tethering";
private static final String TEST_TETHERING_PACKAGE_NAME = "com.prefix.android.tethering";
private static final String TEST_GO_TETHERING_PACKAGE_NAME = "com.prefix.android.go.tethering";
private static final String TEST_RESOLV_PACKAGE_NAME = "com.prefix.android.resolv";
private static final String TEST_GO_RESOLV_PACKAGE_NAME = "com.prefix.android.go.resolv";
private static final String TEST_CONNRES_PACKAGE_NAME =
"com.prefix.android.connectivity.resources";
private static final String TEST_NETWORKSTACK_NAME = "com.prefix.android.networkstack";
private static final String TEST_GO_NETWORKSTACK_NAME = "com.prefix.android.go.networkstack";
private final PackageInfo mPackageInfo = new PackageInfo();
private final PackageInfo mApexPackageInfo = new PackageInfo();
private final PackageInfo mGoApexPackageInfo = new PackageInfo();
private final PackageInfo mTetheringApexPackageInfo = new PackageInfo();
private final PackageInfo mResolvApexPackageInfo = new PackageInfo();
private MockitoSession mSession;
@Mock private Context mContext;
@ -105,13 +112,22 @@ public class DeviceConfigUtilsTest {
mSession = mockitoSession().spyStatic(DeviceConfig.class).startMocking();
mPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
mApexPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
mTetheringApexPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
mGoApexPackageInfo.setLongVersionCode(TEST_GO_PACKAGE_VERSION);
mResolvApexPackageInfo.setLongVersionCode(TEST_RESOLV_PACKAGE_VERSION);
doReturn(mPm).when(mContext).getPackageManager();
doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt());
doReturn(mPackageInfo).when(mPm).getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt());
doReturn(mApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_APEX_PACKAGE_NAME), anyInt());
doReturn(mTetheringApexPackageInfo).when(mPm).getPackageInfo(
eq(TEST_TETHERING_PACKAGE_NAME), anyInt());
doReturn(mResolvApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_RESOLV_PACKAGE_NAME),
anyInt());
doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_TETHERING_PACKAGE_NAME),
anyInt());
doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_RESOLV_PACKAGE_NAME),
anyInt());
doReturn(mResources).when(mContext).getResources();
@ -342,9 +358,9 @@ public class DeviceConfigUtilsTest {
@Test
public void testFeatureIsEnabledOnGo() throws Exception {
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(
eq(TEST_APEX_PACKAGE_NAME), anyInt());
doReturn(mApexPackageInfo).when(mPm).getPackageInfo(
eq(TEST_GO_APEX_PACKAGE_NAME), anyInt());
eq(TEST_TETHERING_PACKAGE_NAME), anyInt());
doReturn(mTetheringApexPackageInfo).when(mPm).getPackageInfo(
eq(TEST_GO_TETHERING_PACKAGE_NAME), anyInt());
doReturn("0").when(() -> DeviceConfig.getProperty(
NAMESPACE_CONNECTIVITY, TEST_EXPERIMENT_FLAG));
doReturn("0").when(() -> DeviceConfig.getProperty(
@ -483,6 +499,31 @@ public class DeviceConfigUtilsTest {
mContext, 889900000L + CONNECTIVITY_MODULE_ID));
}
@Test
public void testIsFeatureSupported_resolvFeature() throws Exception {
assertTrue(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_RESOLV_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
// Return false because feature requires a future version.
assertFalse(DeviceConfigUtils.isFeatureSupported(
mContext, 889900000L + DNS_RESOLVER_MODULE_ID));
}
@Test
public void testIsFeatureSupported_goResolvFeature() throws Exception {
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(eq(TEST_RESOLV_PACKAGE_NAME),
anyInt());
doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_RESOLV_PACKAGE_NAME),
anyInt());
assertFalse(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_RESOLV_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
assertTrue(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_GO_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
// Return false because feature requires a future version.
assertFalse(DeviceConfigUtils.isFeatureSupported(
mContext, 889900000L + DNS_RESOLVER_MODULE_ID));
}
@Test
public void testIsFeatureSupported_illegalModule() throws Exception {
assertThrows(IllegalArgumentException.class,