From d439f790c3a6904f1ed4bd693aa7e388b601c534 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 23 Oct 2019 11:15:10 -0700 Subject: [PATCH 01/15] Add additional fields to VpnProfile for profile-based IKEv2/IPsec VPNs This commit adds the fields required to support IKEv2/IPsec VPNs. Other fields will be reused where possible. Bug: 143221465 Test: Compiles, new tests written for parcel/unparcel, encode/decode Change-Id: I4c0e8fb934e75548424a15bbfb35c2ea9a3a57bc --- .../android/internal/net/VpnProfileTest.java | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 tests/net/java/com/android/internal/net/VpnProfileTest.java diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java new file mode 100644 index 0000000000..8a4b53343c --- /dev/null +++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2019 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.internal.net; + +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.IpSecAlgorithm; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +/** Unit tests for {@link VpnProfile}. */ +@SmallTest +@RunWith(JUnit4.class) +public class VpnProfileTest { + private static final String DUMMY_PROFILE_KEY = "Test"; + + @Test + public void testDefaults() throws Exception { + final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY); + + assertEquals(DUMMY_PROFILE_KEY, p.key); + assertEquals("", p.name); + assertEquals(VpnProfile.TYPE_PPTP, p.type); + assertEquals("", p.server); + assertEquals("", p.username); + assertEquals("", p.password); + assertEquals("", p.dnsServers); + assertEquals("", p.searchDomains); + assertEquals("", p.routes); + assertTrue(p.mppe); + assertEquals("", p.l2tpSecret); + assertEquals("", p.ipsecIdentifier); + assertEquals("", p.ipsecSecret); + assertEquals("", p.ipsecUserCert); + assertEquals("", p.ipsecCaCert); + assertEquals("", p.ipsecServerCert); + assertEquals(null, p.proxy); + assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty()); + assertFalse(p.isBypassable); + assertFalse(p.isMetered); + assertEquals(1400, p.maxMtu); + assertFalse(p.areAuthParamsInline); + } + + private VpnProfile getSampleIkev2Profile(String key) { + final VpnProfile p = new VpnProfile(key); + + p.name = "foo"; + p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + p.server = "bar"; + p.username = "baz"; + p.password = "qux"; + p.dnsServers = "8.8.8.8"; + p.searchDomains = ""; + p.routes = "0.0.0.0/0"; + p.mppe = false; + p.l2tpSecret = ""; + p.ipsecIdentifier = "quux"; + p.ipsecSecret = "quuz"; + p.ipsecUserCert = "corge"; + p.ipsecCaCert = "grault"; + p.ipsecServerCert = "garply"; + p.proxy = null; + p.setAllowedAlgorithms( + Arrays.asList( + IpSecAlgorithm.AUTH_CRYPT_AES_GCM, + IpSecAlgorithm.AUTH_HMAC_SHA512, + IpSecAlgorithm.CRYPT_AES_CBC)); + p.isBypassable = true; + p.isMetered = true; + p.maxMtu = 1350; + p.areAuthParamsInline = true; + + // Not saved, but also not compared. + p.saveLogin = true; + + return p; + } + + @Test + public void testEquals() { + assertEquals( + getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY)); + + final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + modified.maxMtu--; + assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22); + } + + @Test + public void testSetInvalidAlgorithmValueDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetInvalidAlgorithmListDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testEncodeDecode() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertEquals(profile, decoded); + } + + @Test + public void testEncodeDecodeTooManyValues() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final byte[] tooManyValues = + (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes(); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues)); + } + + @Test + public void testEncodeDecodeInvalidNumberOfValues() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final String encoded = new String(profile.encode()); + final byte[] tooFewValues = + encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes(); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues)); + } + + @Test + public void testEncodeDecodeLoginsNotSaved() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + profile.saveLogin = false; + + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertNotEquals(profile, decoded); + + // Add the username/password back, everything else must be equal. + decoded.username = profile.username; + decoded.password = profile.password; + assertEquals(profile, decoded); + } +} From 56420433679a668713ed15677d241ce5b6969976 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Fri, 1 Nov 2019 16:45:08 -0700 Subject: [PATCH 02/15] Add Ikev2VpnProfile as public API This change adds configuration classes for Platform VPNs, with the extensibility for further platform VPNs to be added in future. Profile validity is checked upon construction, and upon changing conversion from VpnProfile instances. Internal storage and method calls all use VpnProfiles to allow for reuse of existing infrastructure. However, when Platform VPN implementations are started, the internal VpnProfile will be converted back into the respective PlatformVpnProfile for validity checking. Bug: 143325939 Test: Compiles, FrameworksNetTests passing. Change-Id: I3c375fb08c132fc062e893c375f5c36460122162 --- core/java/android/net/LinkProperties.java | 3 +- .../java/android/net/Ikev2VpnProfileTest.java | 360 ++++++++++++++++++ 2 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 tests/net/java/android/net/Ikev2VpnProfileTest.java diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index ec773ef4a1..d25ee0e69e 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -80,7 +80,8 @@ public final class LinkProperties implements Parcelable { private final transient boolean mParcelSensitiveFields; private static final int MIN_MTU = 68; - private static final int MIN_MTU_V6 = 1280; + /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */ + static final int MIN_MTU_V6 = 1280; private static final int MAX_MTU = 10000; private static final int INET6_ADDR_LENGTH = 16; diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java new file mode 100644 index 0000000000..d6a2176d7e --- /dev/null +++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2019 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 android.net; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.net.VpnProfile; +import com.android.org.bouncycastle.x509.X509V1CertificateGenerator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import javax.security.auth.x500.X500Principal; + +/** Unit tests for {@link Ikev2VpnProfile.Builder}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class Ikev2VpnProfileTest { + private static final String SERVER_ADDR_STRING = "1.2.3.4"; + private static final String IDENTITY_STRING = "Identity"; + private static final String USERNAME_STRING = "username"; + private static final String PASSWORD_STRING = "pa55w0rd"; + private static final String EXCL_LIST = "exclList"; + private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); + private static final int TEST_MTU = 1300; + + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST); + + private X509Certificate mUserCert; + private X509Certificate mServerRootCa; + private PrivateKey mPrivateKey; + + @Before + public void setUp() throws Exception { + mServerRootCa = generateRandomCertAndKeyPair().cert; + + final CertificateAndKey userCertKey = generateRandomCertAndKeyPair(); + mUserCert = userCertKey.cert; + mPrivateKey = userCertKey.key; + } + + private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() { + final Ikev2VpnProfile.Builder builder = + new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING); + + builder.setBypassable(true); + builder.setProxy(mProxy); + builder.setMaxMtu(TEST_MTU); + builder.setMetered(true); + + return builder; + } + + @Test + public void testBuildValidProfileWithOptions() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + // Check non-auth parameters correctly stored + assertEquals(SERVER_ADDR_STRING, profile.getServerAddr()); + assertEquals(IDENTITY_STRING, profile.getUserIdentity()); + assertEquals(mProxy, profile.getProxyInfo()); + assertTrue(profile.isBypassable()); + assertTrue(profile.isMetered()); + assertEquals(TEST_MTU, profile.getMaxMtu()); + } + + @Test + public void testBuildUsernamePasswordProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(USERNAME_STRING, profile.getUsername()); + assertEquals(PASSWORD_STRING, profile.getPassword()); + assertEquals(mServerRootCa, profile.getServerRootCaCert()); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildDigitalSignatureProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(profile.getUserCert(), mUserCert); + assertEquals(mPrivateKey, profile.getRsaPrivateKey()); + assertEquals(profile.getServerRootCaCert(), mServerRootCa); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + } + + @Test + public void testBuildPresharedKeyProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertArrayEquals(PSK_BYTES, profile.getPresharedKey()); + + assertNull(profile.getServerRootCaCert()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildNoAuthMethodSet() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.build(); + fail("Expected exception due to lack of auth method"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testBuildInvalidMtu() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.setMaxMtu(500); + fail("Expected exception due to too-small MTU"); + } catch (IllegalArgumentException expected) { + } + } + + private void verifyVpnProfileCommon(VpnProfile profile) { + assertEquals(SERVER_ADDR_STRING, profile.server); + assertEquals(IDENTITY_STRING, profile.ipsecIdentifier); + assertEquals(mProxy, profile.proxy); + assertTrue(profile.isBypassable); + assertTrue(profile.isMetered); + assertEquals(TEST_MTU, profile.maxMtu); + } + + @Test + public void testPskConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecCaCert); + } + + @Test + public void testUsernamePasswordConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(USERNAME_STRING, profile.username); + assertEquals(PASSWORD_STRING, profile.password); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecSecret); + } + + @Test + public void testRsaConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert); + assertEquals( + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()), + profile.ipsecSecret); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + } + + @Test + public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + assertNull(result.getServerRootCaCert()); + } + + @Test + public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.ipsecSecret = new String(PSK_BYTES); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getPresharedKey()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + } + + @Test + public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getPresharedKey()); + } + + @Test + public void testPskConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testUsernamePasswordConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testRsaConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + private static class CertificateAndKey { + public final X509Certificate cert; + public final PrivateKey key; + + CertificateAndKey(X509Certificate cert, PrivateKey key) { + this.cert = cert; + this.key = key; + } + } + + private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception { + final Date validityBeginDate = + new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L)); + final Date validityEndDate = + new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L)); + + // Generate a keypair + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(512); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + final X500Principal dnName = new X500Principal("CN=test.android.com"); + final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); + certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); + certGen.setSubjectDN(dnName); + certGen.setIssuerDN(dnName); + certGen.setNotBefore(validityBeginDate); + certGen.setNotAfter(validityEndDate); + certGen.setPublicKey(keyPair.getPublic()); + certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL"); + return new CertificateAndKey(cert, keyPair.getPrivate()); + } +} From 80240acd3f3f1332022d81d549fcb52d872c9c5a Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Fri, 1 Nov 2019 16:46:28 -0700 Subject: [PATCH 03/15] Add VpnManger API surface This change adds the VpnManager, which will be used by apps to install profiles for all platform VPN types (currently only IKEv2). Bug: 143325939 Test: Compiles, FrameworksNetTests passing. Change-Id: I57f854d0a5b18358f3541c24ca0cd8aed03fd7a1 --- .../net/java/android/net/VpnManagerTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tests/net/java/android/net/VpnManagerTest.java diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java new file mode 100644 index 0000000000..655c4d1185 --- /dev/null +++ b/tests/net/java/android/net/VpnManagerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 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 android.net; + +import static org.mockito.Mockito.mock; + +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link VpnManager}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VpnManagerTest { + private static final String VPN_PROFILE_KEY = "KEY"; + + private IConnectivityManager mMockCs; + private VpnManager mVpnManager; + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + + @Before + public void setUp() throws Exception { + mMockCs = mock(IConnectivityManager.class); + mVpnManager = new VpnManager(mMockContext, mMockCs); + } + + @Test + public void testProvisionVpnProfile() throws Exception { + try { + mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class)); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testDeleteProvisionedVpnProfile() throws Exception { + try { + mVpnManager.deleteProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testStartProvisionedVpnProfile() throws Exception { + try { + mVpnManager.startProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testStopProvisionedVpnProfile() throws Exception { + try { + mVpnManager.stopProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } +} From ec88fda4a872a26e354f94ac059b2707eec54c5a Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Wed, 22 Jan 2020 20:14:48 -0800 Subject: [PATCH 04/15] Fix generic inferred type argument. Test: compiles. Test: atest FrameworksNetTests Change-Id: I0dffb3f44fb465ac8bae3c41f26176e86f65e825 --- core/java/android/net/ConnectivityDiagnosticsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index a6f9b96269..4d6b1fde75 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -218,7 +218,7 @@ public class ConnectivityDiagnosticsManager { /** Implement the Parcelable interface */ public static final @NonNull Creator CREATOR = - new Creator<>() { + new Creator() { public ConnectivityReport createFromParcel(Parcel in) { return new ConnectivityReport( in.readParcelable(null), From 896939854bd9b8b1cce39c0dd98074daeaa0b447 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Fri, 17 Jan 2020 11:58:36 -0800 Subject: [PATCH 05/15] Add list of Administrator UIDs to NetworkCapabilities. Adds a list of administrator UIDs to NetworkCapabilties. The carrier privilege permission model allows multiple uids to be granted network-management privileges via certificates stored on a SIM card or in CarrierConfigManager. The current NetworkCapabilities only allows a single uid to be stored to track the owner of the network - this change remedies that discrepancy. Bug: 147903575 Test: atest FrameworksNetTests Change-Id: I3169d31e0270c976a720e80363cb268cbafd0455 --- .../java/android/net/NetworkCapabilities.java | 62 +++++++++++++++++++ .../android/server/ConnectivityService.java | 3 + .../android/net/NetworkCapabilitiesTest.java | 2 +- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 8ebd139224..6207661e47 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -35,6 +35,9 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.StringJoiner; @@ -83,6 +86,7 @@ public final class NetworkCapabilities implements Parcelable { mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; mUids = null; mEstablishingVpnAppUid = INVALID_UID; + mAdministratorUids.clear(); mSSID = null; mPrivateDnsBroken = false; } @@ -101,6 +105,7 @@ public final class NetworkCapabilities implements Parcelable { mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; + setAdministratorUids(nc.mAdministratorUids); mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; mPrivateDnsBroken = nc.mPrivateDnsBroken; @@ -832,6 +837,56 @@ public final class NetworkCapabilities implements Parcelable { return mEstablishingVpnAppUid; } + /** + * UIDs of packages that are administrators of this network, or empty if none. + * + *

This field tracks the UIDs of packages that have permission to manage this network. + * + *

Network owners will also be listed as administrators. + * + *

For NetworkCapability instances being sent from the System Server, this value MUST be + * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the + * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+. + */ + private final List mAdministratorUids = new ArrayList<>(); + + /** + * Sets the list of UIDs that are administrators of this network. + * + *

UIDs included in administratorUids gain administrator privileges over this Network. + * Examples of UIDs that should be included in administratorUids are: + *

    + *
  • Carrier apps with privileges for the relevant subscription + *
  • Active VPN apps + *
  • Other application groups with a particular Network-related role + *
+ * + *

In general, user-supplied networks (such as WiFi networks) do not have an administrator. + * + *

An app is granted owner privileges over Networks that it supplies. Owner privileges + * implicitly include administrator privileges. + * + * @param administratorUids the UIDs to be set as administrators of this Network. + * @hide + */ + @SystemApi + public void setAdministratorUids(@NonNull final List administratorUids) { + mAdministratorUids.clear(); + mAdministratorUids.addAll(administratorUids); + } + + /** + * Retrieves the list of UIDs that are administrators of this Network. + * + * @return the List of UIDs that are administrators of this Network + * @hide + */ + @NonNull + @SystemApi + public List getAdministratorUids() { + return Collections.unmodifiableList(mAdministratorUids); + } + /** * Value indicating that link bandwidth is unspecified. * @hide @@ -1471,6 +1526,7 @@ public final class NetworkCapabilities implements Parcelable { public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mNetworkCapabilities); @@ -1484,6 +1540,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeArraySet(mUids); dest.writeString(mSSID); dest.writeBoolean(mPrivateDnsBroken); + dest.writeList(mAdministratorUids); } public static final @android.annotation.NonNull Creator CREATOR = @@ -1504,6 +1561,7 @@ public final class NetworkCapabilities implements Parcelable { null /* ClassLoader, null for default */); netCap.mSSID = in.readString(); netCap.mPrivateDnsBroken = in.readBoolean(); + netCap.setAdministratorUids(in.readArrayList(null)); return netCap; } @Override @@ -1557,6 +1615,10 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid); } + if (!mAdministratorUids.isEmpty()) { + sb.append(" AdministratorUids: ").append(mAdministratorUids); + } + if (null != mSSID) { sb.append(" SSID: ").append(mSSID); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 76c119db0a..11f0a04fa8 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -212,6 +212,7 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; @@ -1634,6 +1635,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (newNc.getNetworkSpecifier() != null) { newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact()); } + newNc.setAdministratorUids(Collections.EMPTY_LIST); return newNc; } @@ -1664,6 +1666,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!checkSettingsPermission()) { nc.setSingleUid(Binder.getCallingUid()); } + nc.setAdministratorUids(Collections.EMPTY_LIST); } private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) { diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 15691127ca..797fd83321 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -271,7 +271,7 @@ public class NetworkCapabilitiesTest { .addCapability(NET_CAPABILITY_NOT_METERED); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertParcelSane(netCap, 12); + assertParcelSane(netCap, 13); } @Test From d6ad3365fae3394bb794236f07d04c1fb5896079 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Mon, 20 Jan 2020 18:03:41 -0800 Subject: [PATCH 06/15] Define Bundle keys for ConnectivityDiagnostics. ConnectivityDiagnosticsManager defines several callbacks that can be invoked for registered ConnectivityDiagnosticsCallback instances. onConnectivityReport and onDataStallSuspected include ConnectivityReport and DataStallReport, respectively, both of which contain a PersistableBundle to be used for passing information from the NetworkStack module to the registered callback. When values will be consistently passed inside these bundles, their keys are defined inside ConnectivityReport and DataStallReport. Bug: 147249364 Test: compiles Test: atest FrameworksNetTests Change-Id: Icc6bd4337865692b6ca7c18eacabff59ba3a5bb9 --- .../net/ConnectivityDiagnosticsManager.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index a6f9b96269..69f3bfe8bf 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringDef; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; @@ -79,6 +80,128 @@ public class ConnectivityDiagnosticsManager { /** Class that includes connectivity information for a specific Network at a specific time. */ public static final class ConnectivityReport implements Parcelable { + /** + * The overall status of the network is that it is invalid; it neither provides + * connectivity nor has been exempted from validation. + */ + public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; + + /** + * The overall status of the network is that it is valid, this may be because it provides + * full Internet access (all probes succeeded), or because other properties of the network + * caused probes not to be run. + */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID + public static final int NETWORK_VALIDATION_RESULT_VALID = 1; + + /** + * The overall status of the network is that it provides partial connectivity; some + * probed services succeeded but others failed. + */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; + public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; + + /** + * Due to the properties of the network, validation was not performed. + */ + public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; + + /** @hide */ + @IntDef( + prefix = {"NETWORK_VALIDATION_RESULT_"}, + value = { + NETWORK_VALIDATION_RESULT_INVALID, + NETWORK_VALIDATION_RESULT_VALID, + NETWORK_VALIDATION_RESULT_PARTIALLY_VALID, + NETWORK_VALIDATION_RESULT_SKIPPED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkValidationResult {} + + /** + * The overall validation result for the Network being reported on. + * + *

The possible values for this key are: + * {@link #NETWORK_VALIDATION_RESULT_INVALID}, + * {@link #NETWORK_VALIDATION_RESULT_VALID}, + * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID}, + * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}. + * + * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED + */ + @NetworkValidationResult + public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; + + /** DNS probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS + public static final int NETWORK_PROBE_DNS = 0x04; + + /** HTTP probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP + public static final int NETWORK_PROBE_HTTP = 0x08; + + /** HTTPS probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; + public static final int NETWORK_PROBE_HTTPS = 0x10; + + /** Captive portal fallback probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK + public static final int NETWORK_PROBE_FALLBACK = 0x20; + + /** Private DNS (DNS over TLS) probd. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS + public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40; + + /** @hide */ + @IntDef( + prefix = {"NETWORK_PROBE_"}, + value = { + NETWORK_PROBE_DNS, + NETWORK_PROBE_HTTP, + NETWORK_PROBE_HTTPS, + NETWORK_PROBE_FALLBACK, + NETWORK_PROBE_PRIVATE_DNS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkProbe {} + + /** + * A bitmask of network validation probes that succeeded. + * + *

The possible bits values reported by this key are: + * {@link #NETWORK_PROBE_DNS}, + * {@link #NETWORK_PROBE_HTTP}, + * {@link #NETWORK_PROBE_HTTPS}, + * {@link #NETWORK_PROBE_FALLBACK}, + * {@link #NETWORK_PROBE_PRIVATE_DNS}. + */ + @NetworkProbe + public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = + "networkProbesSucceeded"; + + /** + * A bitmask of network validation probes that were attempted. + * + *

These probes may have failed or may be incomplete at the time of this report. + * + *

The possible bits values reported by this key are: + * {@link #NETWORK_PROBE_DNS}, + * {@link #NETWORK_PROBE_HTTP}, + * {@link #NETWORK_PROBE_HTTPS}, + * {@link #NETWORK_PROBE_FALLBACK}, + * {@link #NETWORK_PROBE_PRIVATE_DNS}. + */ + @NetworkProbe + public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = + "networkProbesAttemped"; + + /** @hide */ + @StringDef(prefix = {"KEY_"}, value = { + KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, + KEY_NETWORK_PROBES_ATTEMPTED_BITMASK}) + @Retention(RetentionPolicy.SOURCE) + public @interface ConnectivityReportBundleKeys {} + /** The Network for which this ConnectivityReport applied */ @NonNull private final Network mNetwork; @@ -246,6 +369,49 @@ public class ConnectivityDiagnosticsManager { value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS}) public @interface DetectionMethod {} + /** + * This key represents the period in milliseconds over which other included TCP metrics + * were measured. + * + *

This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_TCP_METRICS}. + * + *

This value is an int. + */ + public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = + "tcpMetricsCollectionPeriodMillis"; + + /** + * This key represents the fail rate of TCP packets when the suspected data stall was + * detected. + * + *

This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_TCP_METRICS}. + * + *

This value is an int percentage between 0 and 100. + */ + public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; + + /** + * This key represents the consecutive number of DNS timeouts that have occurred. + * + *

The consecutive count will be reset any time a DNS response is received. + * + *

This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_DNS_EVENTS}. + * + *

This value is an int. + */ + public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = {"KEY_"}, value = { + KEY_TCP_PACKET_FAIL_RATE, + KEY_DNS_CONSECUTIVE_TIMEOUTS + }) + public @interface DataStallReportBundleKeys {} + /** The Network for which this DataStallReport applied */ @NonNull private final Network mNetwork; @@ -315,6 +481,9 @@ public class ConnectivityDiagnosticsManager { /** * Returns a PersistableBundle with additional info for this report. * + *

Gets a bundle with details about the suspected data stall including information + * specific to the monitoring method that detected the data stall. + * * @return PersistableBundle that may contain additional information on the suspected data * stall */ From d19ef10d006a7254f7f1a6ec21b8c748a7c8acbf Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 20 Jan 2020 19:31:56 +0800 Subject: [PATCH 07/15] Allows the caller to specify configuration by TetheringRequest This is initial work to allow caller to pass their prefered configuration to start tethering. Caller may able to specify the downstream interface ipv4 address with dhcp server disabled for static IP configuration, or able to exempt entitlement check if they have permission in follow up CL. Bug: 141256482 Test: -atest TetheringTest -ON/OFF wifi tethering Change-Id: Ic7c3a33195bbd7e72f9b8e73fa148be476b87bf3 --- .../java/android/net/ConnectivityManager.java | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 753e754602..ce8ffab3ca 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,7 +33,9 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.StartTetheringCallback; import android.net.TetheringManager.TetheringEventCallback; +import android.net.TetheringManager.TetheringRequest; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -2452,10 +2454,12 @@ public class ConnectivityManager { * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int tether(String iface) { return getTetheringManager().tether(iface); } @@ -2512,9 +2516,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + * + * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead. * @hide */ @SystemApi + @Deprecated public static abstract class OnStartTetheringCallback { /** * Called when tethering has been successfully started. @@ -2531,9 +2538,12 @@ public class ConnectivityManager { * Convenient overload for * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null * handler to run on the current thread's {@link Looper}. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback) { @@ -2557,26 +2567,44 @@ public class ConnectivityManager { * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller * of the result of trying to tether. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback, Handler handler) { Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null."); - ResultReceiver wrappedCallback = new ResultReceiver(handler) { + final Executor executor = new Executor() { @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); + public void execute(Runnable command) { + if (handler == null) { + command.run(); } else { - callback.onTetheringFailed(); + handler.post(command); } } }; - getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi); + final StartTetheringCallback tetheringCallback = new StartTetheringCallback() { + @Override + public void onTetheringStarted() { + callback.onTetheringStarted(); + } + + @Override + public void onTetheringFailed(final int resultCode) { + callback.onTetheringFailed(); + } + }; + + final TetheringRequest request = new TetheringRequest.Builder(type) + .setSilentProvisioning(!showProvisioningUi).build(); + + getTetheringManager().startTethering(request, executor, tetheringCallback); } /** @@ -2749,10 +2777,12 @@ public class ConnectivityManager { * * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int setUsbTethering(boolean enable) { return getTetheringManager().setUsbTethering(enable); } From 3e7fb383550057d12b4aae62a03118aa2861df89 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Tue, 17 Dec 2019 16:46:11 -0800 Subject: [PATCH 08/15] Define ConnectivityDiagnosticsCallback IBinder impl. An IBinder implementation of the ConnectivityDiagnosticsCallback AIDL is needed so that apps can receive notfications with their registered callbacks for callbacks coming from ConnectivityService. Bug: 143187964 Bug: 147848028 Test: compiles Test: atest FrameworksNetTests Change-Id: Ie62678f794d81e1edee68977ec684a911b5070b7 --- .../net/ConnectivityDiagnosticsManager.java | 48 +++++++++++++++++ .../ConnectivityDiagnosticsManagerTest.java | 52 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index 456481386c..b13e4b72aa 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.content.Context; +import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -544,6 +545,53 @@ public class ConnectivityDiagnosticsManager { }; } + /** @hide */ + @VisibleForTesting + public static class ConnectivityDiagnosticsBinder + extends IConnectivityDiagnosticsCallback.Stub { + @NonNull private final ConnectivityDiagnosticsCallback mCb; + @NonNull private final Executor mExecutor; + + /** @hide */ + @VisibleForTesting + public ConnectivityDiagnosticsBinder( + @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) { + this.mCb = cb; + this.mExecutor = executor; + } + + /** @hide */ + @VisibleForTesting + public void onConnectivityReport(@NonNull ConnectivityReport report) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onConnectivityReport(report); + }); + }); + } + + /** @hide */ + @VisibleForTesting + public void onDataStallSuspected(@NonNull DataStallReport report) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onDataStallSuspected(report); + }); + }); + } + + /** @hide */ + @VisibleForTesting + public void onNetworkConnectivityReported( + @NonNull Network network, boolean hasConnectivity) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onNetworkConnectivityReported(network, hasConnectivity); + }); + }); + } + } + /** * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about * network connectivity events. Must be extended by applications wanting notifications. diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java index 065add4fc2..7ab4b56fae 100644 --- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java +++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java @@ -16,6 +16,8 @@ package android.net; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsBinder; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import static android.net.ConnectivityDiagnosticsManager.DataStallReport; @@ -25,12 +27,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.os.PersistableBundle; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; + +import java.util.concurrent.Executor; @RunWith(JUnit4.class) public class ConnectivityDiagnosticsManagerTest { @@ -41,6 +50,19 @@ public class ConnectivityDiagnosticsManagerTest { private static final String BUNDLE_KEY = "key"; private static final String BUNDLE_VALUE = "value"; + private static final Executor INLINE_EXECUTOR = x -> x.run(); + + @Mock private ConnectivityDiagnosticsCallback mCb; + + private ConnectivityDiagnosticsBinder mBinder; + + @Before + public void setUp() { + mCb = mock(ConnectivityDiagnosticsCallback.class); + + mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR); + } + private ConnectivityReport createSampleConnectivityReport() { final LinkProperties linkProperties = new LinkProperties(); linkProperties.setInterfaceName(INTERFACE_NAME); @@ -193,4 +215,34 @@ public class ConnectivityDiagnosticsManagerTest { public void testDataStallReportParcelUnparcel() { assertParcelSane(createSampleDataStallReport(), 4); } + + @Test + public void testConnectivityDiagnosticsCallbackOnConnectivityReport() { + mBinder.onConnectivityReport(createSampleConnectivityReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() { + mBinder.onDataStallSuspected(createSampleDataStallReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() { + final Network n = new Network(NET_ID); + final boolean connectivity = true; + + mBinder.onNetworkConnectivityReported(n, connectivity); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity)); + } } From d199a9db39fd9e11a87f98f598654f3a90629f8c Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Tue, 17 Dec 2019 12:55:28 -0800 Subject: [PATCH 09/15] Add AIDL for registering ConnectivityDiagnosticCallbacks. ConnectivityService needs to allow for registering and unregistering ConnectivityDiagnosticsCallbacks as part of ConnectivityDiagnostics API. These methods will be used by ConnectivityDiagnosticsManager so that privileged applications can receive notifications on network events by the system. Bug: 146444622 Test: compiles Change-Id: Id00b30af5692db5eef47d0d948897148578d3b32 --- core/java/android/net/IConnectivityManager.aidl | 5 +++++ .../com/android/server/ConnectivityService.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 186196bd31..3e9e7faccb 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.app.PendingIntent; import android.net.ConnectionInfo; +import android.net.IConnectivityDiagnosticsCallback; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkAgentConfig; @@ -211,5 +212,9 @@ interface IConnectivityManager boolean isCallerCurrentAlwaysOnVpnApp(); boolean isCallerCurrentAlwaysOnVpnLockdownApp(); + void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback, + in NetworkRequest request); + void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback); + IBinder startOrGetTestNetworkService(); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 11f0a04fa8..0981cd8872 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -66,6 +66,7 @@ import android.net.CaptivePortal; import android.net.ConnectionInfo; import android.net.ConnectivityManager; import android.net.ICaptivePortal; +import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; import android.net.IDnsResolver; import android.net.IIpConnectivityMetrics; @@ -7307,4 +7308,20 @@ public class ConnectivityService extends IConnectivityManager.Stub return mTNS; } } + + @Override + public void registerConnectivityDiagnosticsCallback( + @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) { + // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality + throw new UnsupportedOperationException( + "registerConnectivityDiagnosticsCallback not yet implemented"); + } + + @Override + public void unregisterConnectivityDiagnosticsCallback( + @NonNull IConnectivityDiagnosticsCallback callback) { + // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality + throw new UnsupportedOperationException( + "unregisterConnectivityDiagnosticsCallback not yet implemented"); + } } From 5cfe856af1a8bc96bb9e49603ed7ad7ac3eaeaa5 Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Thu, 23 Jan 2020 18:03:46 -0800 Subject: [PATCH 10/15] Fix javadoc build break s/line/link/ Bug: 148248781 Test: compilation Change-Id: I9df208a9b063b71b2f6cc3099005d1ab38044cd2 --- core/java/android/net/ConnectivityManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 753e754602..3d6cae205b 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2602,7 +2602,7 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead. + * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead. * @hide */ @SystemApi @@ -2632,7 +2632,7 @@ public class ConnectivityManager { * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. * - * @deprecated Use {@line TetheringManager#registerTetheringEventCallback} instead. + * @deprecated Use {@link TetheringManager#registerTetheringEventCallback} instead. * @hide */ @SystemApi From d40e5498d0c559503bfdf0815e17e583357f3941 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 23 Jan 2020 22:09:29 -0800 Subject: [PATCH 11/15] Disabled the tests Will re-enable after fixing the negative time issue Test: LinkAddressTest Bug: 148257086 Change-Id: Ia6ce0608a2ce95be980e4f9ea62315076c4453be --- tests/net/common/java/android/net/LinkAddressTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index 096f7bdca0..e566e44c81 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -326,6 +326,7 @@ public class LinkAddressTest { assertParcelSane(l, 6); } + /* @Test public void testDeprecationTime() { try { @@ -392,7 +393,7 @@ public class LinkAddressTest { SystemClock.elapsedRealtime() + 100000); // Check if the permanent flag is removed assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0); - } + }*/ private void assertGlobalPreferred(LinkAddress l, String msg) { From 6ae63e5232041d67c3944bfc85ad53bc2df5a50a Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 21 Jan 2020 13:11:06 +0800 Subject: [PATCH 12/15] Make TetheringManager to system API Also deprecated tethering APIs in ConnectivityManager. Will have follow up change to remove @hide tethering function in ConnectivityManager. Bug: 145093446 Bug: 148038547 Test: -build, flash, boot -atest TetheringTests Change-Id: Ia432057bf9056727c4a0ca97d160a49274d33581 Merged-In: Ia432057bf9056727c4a0ca97d160a49274d33581 --- .../java/android/net/ConnectivityManager.java | 198 ++++++++++++++---- 1 file changed, 162 insertions(+), 36 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index e8740c8e5d..5619857f5b 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,6 +33,7 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.TetheringEventCallback; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -58,6 +59,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; @@ -75,6 +77,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -484,34 +487,35 @@ public class ConnectivityManager { * enable if any. * @hide */ - public static final String EXTRA_ADD_TETHER_TYPE = TetheringManager.EXTRA_ADD_TETHER_TYPE; + public static final String EXTRA_ADD_TETHER_TYPE = TetheringConstants.EXTRA_ADD_TETHER_TYPE; /** * Extra used for communicating with the TetherService. Includes the type of tethering for * which to cancel provisioning. * @hide */ - public static final String EXTRA_REM_TETHER_TYPE = TetheringManager.EXTRA_REM_TETHER_TYPE; + public static final String EXTRA_REM_TETHER_TYPE = TetheringConstants.EXTRA_REM_TETHER_TYPE; /** * Extra used for communicating with the TetherService. True to schedule a recheck of tether * provisioning. * @hide */ - public static final String EXTRA_SET_ALARM = TetheringManager.EXTRA_SET_ALARM; + public static final String EXTRA_SET_ALARM = TetheringConstants.EXTRA_SET_ALARM; /** * Tells the TetherService to run a provision check now. * @hide */ - public static final String EXTRA_RUN_PROVISION = TetheringManager.EXTRA_RUN_PROVISION; + public static final String EXTRA_RUN_PROVISION = TetheringConstants.EXTRA_RUN_PROVISION; /** * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} * which will receive provisioning results. Can be left empty. * @hide */ - public static final String EXTRA_PROVISION_CALLBACK = TetheringManager.EXTRA_PROVISION_CALLBACK; + public static final String EXTRA_PROVISION_CALLBACK = + TetheringConstants.EXTRA_PROVISION_CALLBACK; /** * The absence of a connection type. @@ -2369,10 +2373,12 @@ public class ConnectivityManager { * * @return an array of 0 or more Strings of tetherable interface names. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableIfaces() { return getTetheringManager().getTetherableIfaces(); } @@ -2382,10 +2388,12 @@ public class ConnectivityManager { * * @return an array of 0 or more String of currently tethered interface names. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetheredIfaces() { return getTetheringManager().getTetheredIfaces(); } @@ -2401,10 +2409,12 @@ public class ConnectivityManager { * @return an array of 0 or more String indicating the interface names * which failed to tether. * + * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetheringErroredIfaces() { return getTetheringManager().getTetheringErroredIfaces(); } @@ -2413,9 +2423,11 @@ public class ConnectivityManager { * Get the set of tethered dhcp ranges. * * @return an array of 0 or more {@code String} of tethered dhcp ranges. + * @deprecated This API just return the default value which is not used in DhcpServer. * {@hide} */ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @Deprecated public String[] getTetheredDhcpRanges() { return getTetheringManager().getTetheredDhcpRanges(); } @@ -2468,6 +2480,7 @@ public class ConnectivityManager { * {@hide} */ @UnsupportedAppUsage + @Deprecated public int untether(String iface) { return getTetheringManager().untether(iface); } @@ -2488,6 +2501,7 @@ public class ConnectivityManager { * * @return a boolean - {@code true} indicating Tethering is supported. * + * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead. * {@hide} */ @SystemApi @@ -2574,9 +2588,12 @@ public class ConnectivityManager { * {@link ConnectivityManager.TETHERING_WIFI}, * {@link ConnectivityManager.TETHERING_USB}, or * {@link ConnectivityManager.TETHERING_BLUETOOTH}. + * + * @deprecated Use {@link TetheringManager#stopTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int type) { getTetheringManager().stopTethering(type); @@ -2586,9 +2603,11 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - *@hide + * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead. + * @hide */ @SystemApi + @Deprecated public abstract static class OnTetheringEventCallback { /** @@ -2601,6 +2620,10 @@ public class ConnectivityManager { public void onUpstreamChanged(@Nullable Network network) {} } + @GuardedBy("mTetheringEventCallbacks") + private final ArrayMap + mTetheringEventCallbacks = new ArrayMap<>(); + /** * Start listening to tethering change events. Any new added callback will receive the last * tethering status right away. If callback is registered when tethering has no upstream or @@ -2609,16 +2632,30 @@ public class ConnectivityManager { * * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. + * + * @deprecated Use {@line TetheringManager#registerTetheringEventCallback} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback( @NonNull @CallbackExecutor Executor executor, @NonNull final OnTetheringEventCallback callback) { Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null."); - getTetheringManager().registerTetheringEventCallback(executor, callback); + final TetheringEventCallback tetherCallback = + new TetheringEventCallback() { + @Override + public void onUpstreamChanged(@Nullable Network network) { + callback.onUpstreamChanged(network); + } + }; + + synchronized (mTetheringEventCallbacks) { + mTetheringEventCallbacks.put(callback, tetherCallback); + getTetheringManager().registerTetheringEventCallback(executor, tetherCallback); + } } /** @@ -2626,13 +2663,21 @@ public class ConnectivityManager { * {@link #registerTetheringEventCallback}. * * @param callback previously registered callback. + * + * @deprecated Use {@link TetheringManager#unregisterTetheringEventCallback} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback( @NonNull final OnTetheringEventCallback callback) { - getTetheringManager().unregisterTetheringEventCallback(callback); + Objects.requireNonNull(callback, "The callback must be non-null"); + synchronized (mTetheringEventCallbacks) { + final TetheringEventCallback tetherCallback = + mTetheringEventCallbacks.remove(callback); + getTetheringManager().unregisterTetheringEventCallback(tetherCallback); + } } @@ -2644,10 +2689,12 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable usb interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableUsbRegexs() { return getTetheringManager().getTetherableUsbRegexs(); } @@ -2660,10 +2707,12 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable wifi interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableWifiRegexs() { return getTetheringManager().getTetherableWifiRegexs(); } @@ -2676,10 +2725,13 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable bluetooth interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged( + *TetheringManager.TetheringInterfaceRegexps)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableBluetoothRegexs() { return getTetheringManager().getTetherableBluetoothRegexs(); } @@ -2706,37 +2758,104 @@ public class ConnectivityManager { return getTetheringManager().setUsbTethering(enable); } - /** {@hide} */ + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_NO_ERROR = 0; - /** {@hide} */ - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - /** {@hide} */ - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - /** {@hide} */ - public static final int TETHER_ERROR_UNSUPPORTED = 3; - /** {@hide} */ - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - /** {@hide} */ - public static final int TETHER_ERROR_MASTER_ERROR = 5; - /** {@hide} */ - public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; - /** {@hide} */ - public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - /** {@hide} */ - public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; - /** {@hide} */ - public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; - /** {@hide} */ - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNKNOWN_IFACE = + TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_SERVICE_UNAVAIL}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_SERVICE_UNAVAIL = + TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNSUPPORTED}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNSUPPORTED = TetheringManager.TETHER_ERROR_UNSUPPORTED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNAVAIL_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNAVAIL_IFACE = + TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_TETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNTETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_ENABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DISABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_IFACE_CFG_ERROR = + TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_PROVISION_FAILED = 11; - /** {@hide} */ - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_PROVISION_FAILED = + TetheringManager.TETHER_ERROR_PROVISION_FAILED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DHCPSERVER_ERROR = + TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENTITLEMENT_UNKNOWN}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; + @Deprecated + public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = + TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; /** * Get a more detailed error code after a Tethering or Untethering @@ -2746,10 +2865,12 @@ public class ConnectivityManager { * @return error The error code of the last error tethering or untethering the named * interface * + * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public int getLastTetherError(String iface) { return getTetheringManager().getLastTetherError(iface); } @@ -2767,9 +2888,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether * entitlement succeeded. + * + * @deprecated Use {@link TetheringManager#OnTetheringEntitlementResultListener} instead. * @hide */ @SystemApi + @Deprecated public interface OnTetheringEntitlementResultListener { /** * Called to notify entitlement result. @@ -2799,9 +2923,11 @@ public class ConnectivityManager { * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to * notify the caller of the result of entitlement check. The listener may be called zero * or one time. + * @deprecated Use {@link TetheringManager#requestLatestTetheringEntitlementResult} instead. * {@hide} */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int type, boolean showEntitlementUi, @NonNull @CallbackExecutor Executor executor, From 91c78e57dfda5977915791917b7049925aa156d0 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 20 Jan 2020 19:31:56 +0800 Subject: [PATCH 13/15] Allows the caller to specify configuration by TetheringRequest This is initial work to allow caller to pass their prefered configuration to start tethering. Caller may able to specify the downstream interface ipv4 address with dhcp server disabled for static IP configuration, or able to exempt entitlement check if they have permission in follow up CL. Bug: 141256482 Test: -atest TetheringTest -ON/OFF wifi tethering Change-Id: Ic7c3a33195bbd7e72f9b8e73fa148be476b87bf3 Merged-In: Ic7c3a33195bbd7e72f9b8e73fa148be476b87bf3 --- .../java/android/net/ConnectivityManager.java | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 5619857f5b..cfa98af315 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,7 +33,9 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.StartTetheringCallback; import android.net.TetheringManager.TetheringEventCallback; +import android.net.TetheringManager.TetheringRequest; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -2453,10 +2455,12 @@ public class ConnectivityManager { * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int tether(String iface) { return getTetheringManager().tether(iface); } @@ -2513,9 +2517,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + * + * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead. * @hide */ @SystemApi + @Deprecated public static abstract class OnStartTetheringCallback { /** * Called when tethering has been successfully started. @@ -2532,9 +2539,12 @@ public class ConnectivityManager { * Convenient overload for * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null * handler to run on the current thread's {@link Looper}. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback) { @@ -2558,26 +2568,44 @@ public class ConnectivityManager { * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller * of the result of trying to tether. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback, Handler handler) { Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null."); - ResultReceiver wrappedCallback = new ResultReceiver(handler) { + final Executor executor = new Executor() { @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); + public void execute(Runnable command) { + if (handler == null) { + command.run(); } else { - callback.onTetheringFailed(); + handler.post(command); } } }; - getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi); + final StartTetheringCallback tetheringCallback = new StartTetheringCallback() { + @Override + public void onTetheringStarted() { + callback.onTetheringStarted(); + } + + @Override + public void onTetheringFailed(final int resultCode) { + callback.onTetheringFailed(); + } + }; + + final TetheringRequest request = new TetheringRequest.Builder(type) + .setSilentProvisioning(!showProvisioningUi).build(); + + getTetheringManager().startTethering(request, executor, tetheringCallback); } /** @@ -2750,10 +2778,12 @@ public class ConnectivityManager { * * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int setUsbTethering(boolean enable) { return getTetheringManager().setUsbTethering(enable); } From bb8da980ae1ce56be5c535276772bbc55a60e459 Mon Sep 17 00:00:00 2001 From: Qingxi Li Date: Fri, 17 Jan 2020 17:54:27 -0800 Subject: [PATCH 14/15] Add API for apps to check if they are the network owner This CL extends NetworkCapabilities#mEstablishingVpnAppUid to the network owner app UID and introduces a new public API to get this owner app's UID. Bug: 142072839 Test: atest FrameworksNetTests Change-Id: Id83cdea62b89b586aff74e51e3fee60e53d37d4c --- .../java/android/net/NetworkCapabilities.java | 49 +++++++++---------- .../android/server/ConnectivityService.java | 30 +++++++++--- .../android/net/NetworkCapabilitiesTest.java | 1 + .../server/ConnectivityServiceTest.java | 18 +++++-- 4 files changed, 62 insertions(+), 36 deletions(-) diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 6207661e47..f94bdb767c 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -26,6 +26,7 @@ import android.net.ConnectivityManager.NetworkCallback; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -58,7 +59,6 @@ import java.util.StringJoiner; */ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; - private static final int INVALID_UID = -1; // Set to true when private DNS is broken. private boolean mPrivateDnsBroken; @@ -85,8 +85,8 @@ public final class NetworkCapabilities implements Parcelable { mTransportInfo = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; mUids = null; - mEstablishingVpnAppUid = INVALID_UID; mAdministratorUids.clear(); + mOwnerUid = Process.INVALID_UID; mSSID = null; mPrivateDnsBroken = false; } @@ -104,8 +104,8 @@ public final class NetworkCapabilities implements Parcelable { mTransportInfo = nc.mTransportInfo; mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy - mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; setAdministratorUids(nc.mAdministratorUids); + mOwnerUid = nc.mOwnerUid; mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; mPrivateDnsBroken = nc.mPrivateDnsBroken; @@ -810,31 +810,26 @@ public final class NetworkCapabilities implements Parcelable { } /** - * UID of the app that manages this network, or INVALID_UID if none/unknown. + * UID of the app that owns this network, or INVALID_UID if none/unknown. * - * This field keeps track of the UID of the app that created this network and is in charge - * of managing it. In the practice, it is used to store the UID of VPN apps so it is named - * accordingly, but it may be renamed if other mechanisms are offered for third party apps - * to create networks. - * - * Because this field is only used in the services side (and to avoid apps being able to - * set this to whatever they want), this field is not parcelled and will not be conserved - * across the IPC boundary. - * @hide + *

This field keeps track of the UID of the app that created this network and is in charge of + * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running + * VPN, or Carrier Service app managing a cellular data connection. */ - private int mEstablishingVpnAppUid = INVALID_UID; + private int mOwnerUid = Process.INVALID_UID; /** - * Set the UID of the managing app. - * @hide + * Set the UID of the owner app. */ - public void setEstablishingVpnAppUid(final int uid) { - mEstablishingVpnAppUid = uid; + public void setOwnerUid(final int uid) { + mOwnerUid = uid; } - /** @hide */ - public int getEstablishingVpnAppUid() { - return mEstablishingVpnAppUid; + /** + * Retrieves the UID of the owner app. + */ + public int getOwnerUid() { + return mOwnerUid; } /** @@ -1157,7 +1152,7 @@ public final class NetworkCapabilities implements Parcelable { * member is null, then the network is not restricted by app UID. If it's an empty list, then * it means nobody can use it. * As a special exception, the app managing this network (as identified by its UID stored in - * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in + * mOwnerUid) can always see this network. This is embodied by a special check in * satisfiedByUids. That still does not mean the network necessarily applies * to the app that manages it as determined by #appliesToUid. *

@@ -1264,7 +1259,7 @@ public final class NetworkCapabilities implements Parcelable { * in the passed nc (representing the UIDs that this network is available to). *

* As a special exception, the UID that created the passed network (as represented by its - * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN + * mOwnerUid field) always satisfies a NetworkRequest requiring it (of LISTEN * or REQUEST types alike), even if the network does not apply to it. That is so a VPN app * can see its own network when it listens for it. *

@@ -1275,7 +1270,7 @@ public final class NetworkCapabilities implements Parcelable { public boolean satisfiedByUids(@NonNull NetworkCapabilities nc) { if (null == nc.mUids || null == mUids) return true; // The network satisfies everything. for (UidRange requiredRange : mUids) { - if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true; + if (requiredRange.contains(nc.mOwnerUid)) return true; if (!nc.appliesToUidRange(requiredRange)) { return false; } @@ -1541,6 +1536,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeString(mSSID); dest.writeBoolean(mPrivateDnsBroken); dest.writeList(mAdministratorUids); + dest.writeInt(mOwnerUid); } public static final @android.annotation.NonNull Creator CREATOR = @@ -1562,6 +1558,7 @@ public final class NetworkCapabilities implements Parcelable { netCap.mSSID = in.readString(); netCap.mPrivateDnsBroken = in.readBoolean(); netCap.setAdministratorUids(in.readArrayList(null)); + netCap.mOwnerUid = in.readInt(); return netCap; } @Override @@ -1611,8 +1608,8 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" Uids: <").append(mUids).append(">"); } } - if (mEstablishingVpnAppUid != INVALID_UID) { - sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid); + if (mOwnerUid != Process.INVALID_UID) { + sb.append(" OwnerUid: ").append(mOwnerUid); } if (!mAdministratorUids.isEmpty()) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 11f0a04fa8..3c5045aba3 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1625,7 +1625,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network)); } - private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions( + @VisibleForTesting + NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions( NetworkCapabilities nc, int callerPid, int callerUid) { final NetworkCapabilities newNc = new NetworkCapabilities(nc); if (!checkSettingsPermission(callerPid, callerUid)) { @@ -1636,9 +1637,23 @@ public class ConnectivityService extends IConnectivityManager.Stub newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact()); } newNc.setAdministratorUids(Collections.EMPTY_LIST); + + maybeSanitizeLocationInfoForCaller(newNc, callerUid); + return newNc; } + private void maybeSanitizeLocationInfoForCaller( + NetworkCapabilities nc, int callerUid) { + // TODO(b/142072839): Conditionally reset the owner UID if the following + // conditions are not met: + // 1. The destination app is the network owner + // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted + // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted + // 3. The user's location toggle is on + nc.setOwnerUid(INVALID_UID); + } + private LinkProperties linkPropertiesRestrictedForCallerPermissions( LinkProperties lp, int callerPid, int callerUid) { if (lp == null) return new LinkProperties(); @@ -1667,6 +1682,9 @@ public class ConnectivityService extends IConnectivityManager.Stub nc.setSingleUid(Binder.getCallingUid()); } nc.setAdministratorUids(Collections.EMPTY_LIST); + + // Clear owner UID; this can never come from an app. + nc.setOwnerUid(INVALID_UID); } private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) { @@ -5794,7 +5812,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } final Set ranges = nai.networkCapabilities.getUids(); - final int vpnAppUid = nai.networkCapabilities.getEstablishingVpnAppUid(); + final int vpnAppUid = nai.networkCapabilities.getOwnerUid(); // TODO: this create a window of opportunity for apps to receive traffic between the time // when the old rules are removed and the time when new rules are added. To fix this, // make eBPF support two whitelisted interfaces so here new rules can be added before the @@ -5993,7 +6011,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nc == null || lp == null) return false; return nai.isVPN() && !nai.networkAgentConfig.allowBypass - && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID + && nc.getOwnerUid() != Process.SYSTEM_UID && lp.getInterfaceName() != null && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()); } @@ -6041,12 +6059,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO Fix this window by computing an accurate diff on Set, so the old range // to be removed will never overlap with the new range to be added. if (wasFiltering && !prevRanges.isEmpty()) { - mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, - prevNc.getEstablishingVpnAppUid()); + mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, prevNc.getOwnerUid()); } if (shouldFilter && !newRanges.isEmpty()) { - mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, - newNc.getEstablishingVpnAppUid()); + mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, newNc.getOwnerUid()); } } catch (Exception e) { // Never crash! diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 797fd83321..3e4f3d8188 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -269,6 +269,7 @@ public class NetworkCapabilitiesTest { .setUids(uids) .addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_NOT_METERED); + netCap.setOwnerUid(123); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); assertParcelSane(netCap, 13); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 783f8d177f..e80b7c9d01 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -6313,12 +6313,24 @@ public class ConnectivityServiceTest { assertEquals(wifiLp, mService.getActiveLinkProperties()); } + @Test + public void testNetworkCapabilitiesRestrictedForCallerPermissions() { + int callerUid = Process.myUid(); + final NetworkCapabilities originalNc = new NetworkCapabilities(); + originalNc.setOwnerUid(callerUid); - private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid, - Set vpnRange) throws Exception { + final NetworkCapabilities newNc = + mService.networkCapabilitiesRestrictedForCallerPermissions( + originalNc, Process.myPid(), callerUid); + + assertEquals(Process.INVALID_UID, newNc.getOwnerUid()); + } + + private TestNetworkAgentWrapper establishVpn( + LinkProperties lp, int ownerUid, Set vpnRange) throws Exception { final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp); - vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid); + vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.connect(); mMockVpn.setUids(vpnRange); From 74f0fb8d4ee9a370e13d1bf0a69a3d22316504c3 Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Thu, 23 Jan 2020 18:03:46 -0800 Subject: [PATCH 15/15] Fix javadoc build break s/line/link/ Bug: 148248781 Test: compilation Change-Id: I9df208a9b063b71b2f6cc3099005d1ab38044cd2 Merged-in: I9df208a9b063b71b2f6cc3099005d1ab38044cd2 --- core/java/android/net/ConnectivityManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index cd88913e5f..94eda01410 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2631,7 +2631,7 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead. + * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead. * @hide */ @SystemApi @@ -2661,7 +2661,7 @@ public class ConnectivityManager { * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. * - * @deprecated Use {@line TetheringManager#registerTetheringEventCallback} instead. + * @deprecated Use {@link TetheringManager#registerTetheringEventCallback} instead. * @hide */ @SystemApi