diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-05 12:15:02 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-05 12:15:02 +0000 |
commit | 18239323b7c96e64593060802d780148af95db4c (patch) | |
tree | 91c86822994d8bc41f0f2e73f865516d138a68b3 | |
parent | e0ca873124dbb1dfc055dfdb628154f9fd1205d1 (diff) | |
parent | 34c4cf53fc0ef46ff0176565b4913fe1aa83c2c3 (diff) | |
download | net-18239323b7c96e64593060802d780148af95db4c.tar.gz |
Snap for 10616795 from 34c4cf53fc0ef46ff0176565b4913fe1aa83c2c3 to mainline-media-releaseaml_med_341011000
Change-Id: If2204a2aa0bd19948fa826b2ae53cef3057f67b2
8 files changed, 275 insertions, 24 deletions
diff --git a/common/Android.bp b/common/Android.bp index e1b5601d..d3e68b75 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -41,6 +41,7 @@ java_library { "device/com/android/net/module/util/PacketReader.java", "device/com/android/net/module/util/SharedLog.java", "device/com/android/net/module/util/SocketUtils.java", + "device/com/android/net/module/util/FeatureVersions.java", // This library is used by system modules, for which the system health impact of Kotlin // has not yet been evaluated. Annotations may need jarjar'ing. // "src_devicecommon/**/*.kt", diff --git a/common/device/com/android/net/module/util/DeviceConfigUtils.java b/common/device/com/android/net/module/util/DeviceConfigUtils.java index 138c1e54..bea227d9 100644 --- a/common/device/com/android/net/module/util/DeviceConfigUtils.java +++ b/common/device/com/android/net/module/util/DeviceConfigUtils.java @@ -17,6 +17,12 @@ package com.android.net.module.util; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +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.MODULE_MASK; +import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID; +import static com.android.net.module.util.FeatureVersions.VERSION_MASK; import android.content.Context; import android.content.Intent; @@ -53,12 +59,14 @@ public final class DeviceConfigUtils { "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK"; private static final String CONNECTIVITY_RES_PKG_DIR = "/apex/" + TETHERING_MODULE_NAME + "/"; - private static final long DEFAULT_PACKAGE_VERSION = 1000; + @VisibleForTesting + public static final long DEFAULT_PACKAGE_VERSION = 1000; @VisibleForTesting public static void resetPackageVersionCacheForTest() { sPackageVersion = -1; sModuleVersion = -1; + sNetworkStackModuleVersion = -1; } private static volatile long sPackageVersion = -1; @@ -224,14 +232,7 @@ public final class DeviceConfigUtils { // If that fails retry by appending "go.tethering" instead private static long resolveTetheringModuleVersion(@NonNull Context context) throws PackageManager.NameNotFoundException { - final String connResourcesPackage = getConnectivityResourcesPackageName(context); - final int pkgPrefixLen = connResourcesPackage.indexOf("connectivity"); - if (pkgPrefixLen < 0) { - throw new IllegalStateException( - "Invalid connectivity resources package: " + connResourcesPackage); - } - - final String pkgPrefix = connResourcesPackage.substring(0, pkgPrefixLen); + final String pkgPrefix = resolvePkgPrefix(context); final PackageManager packageManager = context.getPackageManager(); try { return packageManager.getPackageInfo(pkgPrefix + "tethering", @@ -245,6 +246,17 @@ public final class DeviceConfigUtils { PackageManager.MATCH_APEX).getLongVersionCode(); } + private static String resolvePkgPrefix(Context context) { + final String connResourcesPackage = getConnectivityResourcesPackageName(context); + final int pkgPrefixLen = connResourcesPackage.indexOf("connectivity"); + if (pkgPrefixLen < 0) { + throw new IllegalStateException( + "Invalid connectivity resources package: " + connResourcesPackage); + } + + return connResourcesPackage.substring(0, pkgPrefixLen); + } + private static volatile long sModuleVersion = -1; private static long getTetheringModuleVersion(@NonNull Context context) { if (sModuleVersion >= 0) return sModuleVersion; @@ -260,6 +272,80 @@ public final class DeviceConfigUtils { return sModuleVersion; } + private static volatile long sNetworkStackModuleVersion = -1; + + /** + * Get networkstack module version. + */ + @VisibleForTesting + static long getNetworkStackModuleVersion(@NonNull Context context) { + if (sNetworkStackModuleVersion >= 0) return sNetworkStackModuleVersion; + + try { + sNetworkStackModuleVersion = resolveNetworkStackModuleVersion(context); + } catch (PackageManager.NameNotFoundException e) { + Log.wtf(TAG, "Failed to resolve networkstack module version: " + e); + return DEFAULT_PACKAGE_VERSION; + } + return sNetworkStackModuleVersion; + } + + private static long resolveNetworkStackModuleVersion(@NonNull Context context) + throws PackageManager.NameNotFoundException { + // TODO(b/293975546): Strictly speaking this is the prefix for connectivity and not + // network stack. In practice, it's the same. Read the prefix from network stack instead. + final String pkgPrefix = resolvePkgPrefix(context); + final PackageManager packageManager = context.getPackageManager(); + try { + return packageManager.getPackageInfo(pkgPrefix + "networkstack", + PackageManager.MATCH_SYSTEM_ONLY).getLongVersionCode(); + } catch (PackageManager.NameNotFoundException e) { + Log.d(TAG, "Device is using go or non-mainline modules"); + // fall through + } + + return packageManager.getPackageInfo(pkgPrefix + "go.networkstack", + PackageManager.MATCH_ALL).getLongVersionCode(); + } + + /** + * Check whether one specific feature is supported from the feature Id. The feature Id is + * composed by a module package Id and version Id from {@link FeatureVersions}. + * + * This is useful when a feature required minimal module version supported and cannot function + * well with a standalone newer module. + * @param context The global context information about an app environment. + * @param featureId The feature id that contains required module id and minimal module version + * @return true if this feature is supported, or false if not supported. + **/ + public static boolean isFeatureSupported(@NonNull Context context, long featureId) { + final long moduleVersion; + final long moduleId = featureId & MODULE_MASK; + if (moduleId == CONNECTIVITY_MODULE_ID) { + moduleVersion = getTetheringModuleVersion(context); + } else if (moduleId == NETWORK_STACK_MODULE_ID) { + moduleVersion = getNetworkStackModuleVersion(context); + } else { + throw new IllegalArgumentException("Unknown module " + moduleId); + } + // Support by default if no module version is available. + return moduleVersion == DEFAULT_PACKAGE_VERSION + || moduleVersion >= (featureId & VERSION_MASK); + } + + /** + * Check whether one specific experimental feature in tethering module from {@link DeviceConfig} + * is disabled by setting a non-zero value in the property. + * + * @param name The name of the property to look up. + * @return true if this feature is force disabled, or false if not disabled. + */ + public static boolean isTetheringFeatureForceDisabled(String name) { + final int propertyVersion = getDeviceConfigPropertyInt(NAMESPACE_TETHERING, name, + 0 /* default value */); + return propertyVersion != 0; + } + /** * Gets boolean config from resources. */ diff --git a/common/device/com/android/net/module/util/DomainUtils.java b/common/device/com/android/net/module/util/DomainUtils.java index 80e0b64b..b327fd08 100644 --- a/common/device/com/android/net/module/util/DomainUtils.java +++ b/common/device/com/android/net/module/util/DomainUtils.java @@ -123,7 +123,7 @@ public final class DomainUtils { * @return domain name(s) string array with space separated, or empty string if decode fails. */ @NonNull - public static String[] decode(@NonNull final ByteBuffer buffer, boolean compression) { + public static ArrayList<String> decode(@NonNull final ByteBuffer buffer, boolean compression) { final ArrayList<String> domainList = new ArrayList<>(); while (buffer.remaining() > 0) { try { @@ -138,6 +138,6 @@ public final class DomainUtils { break; } } - return domainList.toArray(new String[0]); + return domainList; } } diff --git a/common/device/com/android/net/module/util/FeatureVersions.java b/common/device/com/android/net/module/util/FeatureVersions.java new file mode 100644 index 00000000..4986a588 --- /dev/null +++ b/common/device/com/android/net/module/util/FeatureVersions.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.net.module.util; + +/** + * Class to centralize feature version control that requires a specific module or a specific + * module version. + * @hide + */ +public class FeatureVersions { + public static final long MODULE_MASK = 0xFF00_000000000L; + public static final long VERSION_MASK = 0x0000_FFFFFFFFFL; + public static final long CONNECTIVITY_MODULE_ID = 0x0100_000000000L; + public static final long NETWORK_STACK_MODULE_ID = 0x0200_000000000L; + // 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. + public static final long FEATURE_CLAT_ADDRESS_TRANSLATE = + NETWORK_STACK_MODULE_ID + 340900000L; +} diff --git a/common/device/com/android/net/module/util/structs/Ipv6PktInfo.java b/common/device/com/android/net/module/util/structs/Ipv6PktInfo.java new file mode 100644 index 00000000..0dccb727 --- /dev/null +++ b/common/device/com/android/net/module/util/structs/Ipv6PktInfo.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.net.module.util.structs; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +import java.net.Inet6Address; + +/** + * structure in6_pktinfo + * + * see also: + * + * include/uapi/linux/ipv6.h + */ +public class Ipv6PktInfo extends Struct { + @Field(order = 0, type = Type.Ipv6Address) + public final Inet6Address addr; // IPv6 source or destination address + @Field(order = 1, type = Type.S32) + public final int ifindex; // interface index + + public Ipv6PktInfo(final Inet6Address addr, final int ifindex) { + this.addr = addr; + this.ifindex = ifindex; + } +} 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 328c39a4..7946244d 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 @@ -17,12 +17,16 @@ package com.android.net.module.util; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +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.NETWORK_STACK_MODULE_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.anyInt; @@ -84,6 +88,8 @@ public class DeviceConfigUtilsTest { private static final String TEST_GO_APEX_PACKAGE_NAME = "com.prefix.android.go.tethering"; 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 MockitoSession mSession; @@ -346,4 +352,82 @@ public class DeviceConfigUtilsTest { doThrow(new Resources.NotFoundException()).when(mResources).getInteger(someResId); assertEquals(2098, DeviceConfigUtils.getResIntegerConfig(mContext, someResId, 2098)); } + + @Test + public void testGetNetworkStackModuleVersionCaching() throws Exception { + final PackageInfo networkStackPackageInfo = new PackageInfo(); + networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION); + doReturn(networkStackPackageInfo).when(mPm).getPackageInfo( + eq(TEST_NETWORKSTACK_NAME), anyInt()); + assertEquals(TEST_PACKAGE_VERSION, + DeviceConfigUtils.getNetworkStackModuleVersion(mContext)); + + assertEquals(TEST_PACKAGE_VERSION, + DeviceConfigUtils.getNetworkStackModuleVersion(mContext)); + // Package info is only queried once + verify(mPm, times(1)).getPackageInfo(anyString(), anyInt()); + verify(mContext, never()).getPackageName(); + } + + @Test + public void testGetNetworkStackModuleVersionOnNonMainline() { + assertEquals(DeviceConfigUtils.DEFAULT_PACKAGE_VERSION, + DeviceConfigUtils.getNetworkStackModuleVersion(mContext)); + } + + @Test + public void testGetNetworkStackModuleVersion() throws Exception { + final PackageInfo networkStackPackageInfo = new PackageInfo(); + final PackageInfo goNetworkStackPackageInfo = new PackageInfo(); + networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION); + goNetworkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION + 1); + doReturn(goNetworkStackPackageInfo).when(mPm).getPackageInfo( + eq(TEST_NETWORKSTACK_NAME), anyInt()); + // Verify the returned value is go module version. + assertEquals(TEST_PACKAGE_VERSION + 1, + DeviceConfigUtils.getNetworkStackModuleVersion(mContext)); + } + + @Test + public void testIsFeatureSupported_networkStackFeature() throws Exception { + // Supported for DEFAULT_PACKAGE_VERSION + assertTrue(DeviceConfigUtils.isFeatureSupported( + mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID)); + + final PackageInfo networkStackPackageInfo = new PackageInfo(); + networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION); + doReturn(networkStackPackageInfo).when(mPm).getPackageInfo( + eq(TEST_NETWORKSTACK_NAME), anyInt()); + + assertTrue(DeviceConfigUtils.isFeatureSupported( + mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID)); + assertFalse(DeviceConfigUtils.isFeatureSupported( + mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID + 1)); + } + + @Test + public void testIsFeatureSupported_tetheringFeature() throws Exception { + assertTrue(DeviceConfigUtils.isFeatureSupported( + mContext, TEST_PACKAGE_VERSION + CONNECTIVITY_MODULE_ID)); + // Return false because feature requires a future version. + assertFalse(DeviceConfigUtils.isFeatureSupported( + mContext, 889900000L + CONNECTIVITY_MODULE_ID)); + } + + @Test + public void testIsFeatureSupported_illegalModule() throws Exception { + assertThrows(IllegalArgumentException.class, + () -> DeviceConfigUtils.isFeatureSupported(mContext, TEST_PACKAGE_VERSION)); + } + + @Test + public void testIsTetheringFeatureForceDisabled() throws Exception { + doReturn("0").when(() -> DeviceConfig.getProperty( + eq(NAMESPACE_TETHERING), eq(TEST_EXPERIMENT_FLAG))); + assertFalse(DeviceConfigUtils.isTetheringFeatureForceDisabled(TEST_EXPERIMENT_FLAG)); + + doReturn(TEST_FLAG_VALUE_STRING).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_TETHERING), eq(TEST_EXPERIMENT_FLAG))); + assertTrue(DeviceConfigUtils.isTetheringFeatureForceDisabled(TEST_EXPERIMENT_FLAG)); + } } diff --git a/common/tests/unit/src/com/android/net/module/util/DomainUtilsTest.java b/common/tests/unit/src/com/android/net/module/util/DomainUtilsTest.java index 606ed5fb..5eaf2add 100644 --- a/common/tests/unit/src/com/android/net/module/util/DomainUtilsTest.java +++ b/common/tests/unit/src/com/android/net/module/util/DomainUtilsTest.java @@ -30,6 +30,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest @@ -168,39 +171,40 @@ public class DomainUtilsTest { @Test public void testDecodeDomainNames() { + ArrayList<String> suffixStringList; String suffixes = "06676F6F676C6503636F6D00" // google.com + "076578616D706C6503636F6D00" // example.com + "06676F6F676C6500"; // google - String[] expected = new String[] {"google.com", "example.com"}; + List<String> expected = Arrays.asList("google.com", "example.com"); ByteBuffer buffer = ByteBuffer.wrap(HexEncoding.decode(suffixes)); - String[] suffixString = DomainUtils.decode(buffer, false /* compression */); - assertArrayEquals(expected, suffixString); + suffixStringList = DomainUtils.decode(buffer, false /* compression */); + assertEquals(expected, suffixStringList); // include suffix with invalid length: 64 suffixes = "06676F6F676C6503636F6D00" // google.com + "406578616D706C6503636F6D00" // example.com(length=64) + "06676F6F676C6500"; // google - expected = new String[] {"google.com"}; + expected = Arrays.asList("google.com"); buffer = ByteBuffer.wrap(HexEncoding.decode(suffixes)); - suffixString = DomainUtils.decode(buffer, false /* compression */); - assertArrayEquals(expected, suffixString); + suffixStringList = DomainUtils.decode(buffer, false /* compression */); + assertEquals(expected, suffixStringList); // include suffix with invalid length: 0 suffixes = "06676F6F676C6503636F6D00" // google.com + "076578616D706C6503636F6D00" // example.com + "00676F6F676C6500"; // google(length=0) - expected = new String[] {"google.com", "example.com"}; + expected = Arrays.asList("google.com", "example.com"); buffer = ByteBuffer.wrap(HexEncoding.decode(suffixes)); - suffixString = DomainUtils.decode(buffer, false /* compression */); - assertArrayEquals(expected, suffixString); + suffixStringList = DomainUtils.decode(buffer, false /* compression */); + assertEquals(expected, suffixStringList); suffixes = "076578616D706C6504636F727006676F6F676C6503636F6D00" // example.corp.google.com + "C008" // corp.google.com + "C00D"; // google.com - expected = new String[] {"example.corp.google.com", "corp.google.com", "google.com"}; + expected = Arrays.asList("example.corp.google.com", "corp.google.com", "google.com"); buffer = ByteBuffer.wrap(HexEncoding.decode(suffixes)); - suffixString = DomainUtils.decode(buffer, true /* compression */); - assertArrayEquals(expected, suffixString); + suffixStringList = DomainUtils.decode(buffer, true /* compression */); + assertEquals(expected, suffixStringList); } } diff --git a/common/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt b/common/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt index 6bcb8fc1..f1f09754 100644 --- a/common/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt +++ b/common/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt @@ -72,7 +72,7 @@ class ConnectivityCheckTest { val cb = TestableNetworkCallback() val cm = context.getSystemService(ConnectivityManager::class.java) ?: fail("Could not get ConnectivityManager") - cm.registerNetworkCallback( + cm.requestNetwork( NetworkRequest.Builder() .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET).build(), cb) |