Merge "Add new team member 'Yuyang Huang' to OWNERS"
diff --git a/Cronet/AndroidManifest.xml b/Cronet/AndroidManifest.xml
index f3b3c3e..c6471ed 100644
--- a/Cronet/AndroidManifest.xml
+++ b/Cronet/AndroidManifest.xml
@@ -17,7 +17,7 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cronet"
+ package="com.android.net.http"
android:versionCode="11"
android:versionName="R-initial">
</manifest>
diff --git a/Cronet/jarjar-rules.txt b/Cronet/jarjar-rules.txt
index 7111ebc..76a799e 100644
--- a/Cronet/jarjar-rules.txt
+++ b/Cronet/jarjar-rules.txt
@@ -1,3 +1,3 @@
-rule androidx.** com.android.cronet.@0
-rule android.support.** com.android.cronet.@0
+rule androidx.** com.android.net.http.@0
+rule android.support.** com.android.net.http.@0
diff --git a/Cronet/tests/cts/AndroidManifest.xml b/Cronet/tests/cts/AndroidManifest.xml
index 6797586..70acb0d 100644
--- a/Cronet/tests/cts/AndroidManifest.xml
+++ b/Cronet/tests/cts/AndroidManifest.xml
@@ -18,7 +18,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.cronet.cts">
+ package="android.net.http.cts">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
@@ -30,8 +30,8 @@
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.cronet.cts"
- android:label="CTS tests of android.cronet">
+ android:targetPackage="android.net.http.cts"
+ android:label="CTS tests of android.net.http">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener"/>
</instrumentation>
diff --git a/Cronet/tests/cts/AndroidTest.xml b/Cronet/tests/cts/AndroidTest.xml
index 3ab60e4..1f6bdb3 100644
--- a/Cronet/tests/cts/AndroidTest.xml
+++ b/Cronet/tests/cts/AndroidTest.xml
@@ -25,7 +25,7 @@
<option name="test-file-name" value="CtsCronetTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.cronet.cts" />
+ <option name="package" value="android.net.http.cts" />
<option name="runtime-hint" value="10s" />
</test>
diff --git a/Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java
similarity index 95%
rename from Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java
rename to Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java
index 7dd9a9a..55f8edd 100644
--- a/Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.chromium.net.test;
+
+package android.net.http.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -21,6 +22,8 @@
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.http.cts.util.TestUrlRequestCallback;
+import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -29,8 +32,6 @@
import org.chromium.net.CronetEngine;
import org.chromium.net.UrlRequest;
import org.chromium.net.UrlResponseInfo;
-import org.chromium.net.test.util.TestUrlRequestCallback;
-import org.chromium.net.test.util.TestUrlRequestCallback.ResponseStep;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
similarity index 99%
rename from Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java
rename to Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
index 3c7c001..c7143f5 100644
--- a/Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.chromium.net.test.util;
+package android.net.http.cts.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 752c347..7669e0e 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -31,6 +31,7 @@
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreferences(@NonNull android.os.UserHandle, @NonNull java.util.List<android.net.ProfileNetworkPreference>, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setUidFirewallRule(int, int, int);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setVpnDefaultForUids(@NonNull String, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
method public void systemReady();
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 1b4b42f..60bc68c 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -1378,6 +1378,17 @@
}
}
+ private static UidRange[] getUidRangeArray(@NonNull Collection<Range<Integer>> ranges) {
+ Objects.requireNonNull(ranges);
+ final UidRange[] rangesArray = new UidRange[ranges.size()];
+ int index = 0;
+ for (Range<Integer> range : ranges) {
+ rangesArray[index++] = new UidRange(range.getLower(), range.getUpper());
+ }
+
+ return rangesArray;
+ }
+
/**
* Adds or removes a requirement for given UID ranges to use the VPN.
*
@@ -1397,6 +1408,12 @@
* {@link NetworkCallback#onBlockedStatusChanged} callbacks called after the changes take
* effect.
* <p>
+ * This method will block the specified UIDs from accessing non-VPN networks, but does not
+ * affect what the UIDs get as their default network.
+ * Compare {@link #setVpnDefaultForUids(String, Collection)}, which declares that the UIDs
+ * should only have a VPN as their default network, but does not block them from accessing other
+ * networks if they request them explicitly with the {@link Network} API.
+ * <p>
* This method should be called only by the VPN code.
*
* @param ranges the UID ranges to restrict
@@ -1416,11 +1433,7 @@
// This method is not necessarily expected to be used outside the system server, so
// parceling may not be necessary, but it could be used out-of-process, e.g., by the network
// stack process, or by tests.
- UidRange[] rangesArray = new UidRange[ranges.size()];
- int index = 0;
- for (Range<Integer> range : ranges) {
- rangesArray[index++] = new UidRange(range.getLower(), range.getUpper());
- }
+ final UidRange[] rangesArray = getUidRangeArray(ranges);
try {
mService.setRequireVpnForUids(requireVpn, rangesArray);
} catch (RemoteException e) {
@@ -1429,6 +1442,41 @@
}
/**
+ * Inform the system that this VPN session should manage the passed UIDs.
+ *
+ * A VPN with the specified session ID may call this method to inform the system that the UIDs
+ * in the specified range are subject to a VPN.
+ * When this is called, the system will only choose a VPN for the default network of the UIDs in
+ * the specified ranges.
+ *
+ * This method declares that the UIDs in the range will only have a VPN for their default
+ * network, but does not block the UIDs from accessing other networks (permissions allowing) by
+ * explicitly requesting it with the {@link Network} API.
+ * Compare {@link #setRequireVpnForUids(boolean, Collection)}, which does not affect what
+ * network the UIDs get as default, but will block them from accessing non-VPN networks.
+ *
+ * @param session The VPN session which manages the passed UIDs.
+ * @param ranges The uid ranges which will treat VPN as their only default network.
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ @SystemApi(client = MODULE_LIBRARIES)
+ public void setVpnDefaultForUids(@NonNull String session,
+ @NonNull Collection<Range<Integer>> ranges) {
+ Objects.requireNonNull(ranges);
+ final UidRange[] rangesArray = getUidRangeArray(ranges);
+ try {
+ mService.setVpnNetworkPreference(session, rangesArray);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Informs ConnectivityService of whether the legacy lockdown VPN, as implemented by
* LockdownVpnTracker, is in use. This is deprecated for new devices starting from Android 12
* but is still supported for backwards compatibility.
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 43d2f07..7b6e769 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -249,4 +249,6 @@
void replaceFirewallChain(int chain, in int[] uids);
IBinder getCompanionDeviceManagerProxyService();
+
+ void setVpnNetworkPreference(String session, in UidRange[] ranges);
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index d19afa4..004b4d2 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -286,6 +286,7 @@
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.UidRangeUtils;
+import com.android.server.connectivity.VpnNetworkPreferenceInfo;
import com.android.server.connectivity.wear.CompanionDeviceManagerProxyService;
import libcore.io.IoUtils;
@@ -747,6 +748,12 @@
private static final int EVENT_USER_DOES_NOT_WANT = 58;
/**
+ * Event to set VPN as preferred network for specific apps.
+ * obj = VpnNetworkPreferenceInfo
+ */
+ private static final int EVENT_SET_VPN_NETWORK_PREFERENCE = 59;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -1625,6 +1632,17 @@
TYPE_NONE, NetworkRequest.Type.REQUEST);
}
+ private NetworkRequest createVpnRequest() {
+ final NetworkCapabilities netCap = new NetworkCapabilities.Builder()
+ .withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_VPN)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build();
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+ return createNetworkRequest(NetworkRequest.Type.REQUEST, netCap);
+ }
+
private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
@@ -5532,6 +5550,9 @@
nai.onPreventAutomaticReconnect();
nai.disconnect();
break;
+ case EVENT_SET_VPN_NETWORK_PREFERENCE:
+ handleSetVpnNetworkPreference((VpnNetworkPreferenceInfo) msg.obj);
+ break;
}
}
}
@@ -7123,6 +7144,12 @@
private NetworkPreferenceList<UserHandle, ProfileNetworkPreferenceInfo>
mProfileNetworkPreferences = new NetworkPreferenceList<>();
+ // Current VPN network preferences. This object follows the same threading rules as the OEM
+ // network preferences above.
+ @NonNull
+ private NetworkPreferenceList<String, VpnNetworkPreferenceInfo>
+ mVpnNetworkPreferences = new NetworkPreferenceList<>();
+
// A set of UIDs that should use mobile data preferentially if available. This object follows
// the same threading rules as the OEM network preferences above.
@NonNull
@@ -11113,6 +11140,60 @@
}
/**
+ * Sets the specified UIDs to get/receive the VPN as the only default network.
+ *
+ * Calling this will overwrite the existing network preference for this session, and the
+ * specified UIDs won't get any default network when no VPN is connected.
+ *
+ * @param session The VPN session which manages the passed UIDs.
+ * @param ranges The uid ranges which will treat VPN as the only preferred network. Clear the
+ * setting for this session if the array is empty. Null is not allowed, the
+ * method will use {@link Objects#requireNonNull(Object)} to check this variable.
+ * @hide
+ */
+ @Override
+ public void setVpnNetworkPreference(String session, UidRange[] ranges) {
+ Objects.requireNonNull(ranges);
+ enforceNetworkStackOrSettingsPermission();
+ final UidRange[] sortedRanges = UidRangeUtils.sortRangesByStartUid(ranges);
+ if (UidRangeUtils.sortedRangesContainOverlap(sortedRanges)) {
+ throw new IllegalArgumentException(
+ "setVpnNetworkPreference: Passed UID ranges overlap");
+ }
+
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_VPN_NETWORK_PREFERENCE,
+ new VpnNetworkPreferenceInfo(session,
+ new ArraySet<UidRange>(Arrays.asList(ranges)))));
+ }
+
+ private void handleSetVpnNetworkPreference(VpnNetworkPreferenceInfo preferenceInfo) {
+ Log.d(TAG, "handleSetVpnNetworkPreference: preferenceInfo = " + preferenceInfo);
+
+ mVpnNetworkPreferences = mVpnNetworkPreferences.minus(preferenceInfo.getKey());
+ mVpnNetworkPreferences = mVpnNetworkPreferences.plus(preferenceInfo);
+
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_VPN);
+ addPerAppDefaultNetworkRequests(createNrisForVpnNetworkPreference(mVpnNetworkPreferences));
+ // Finally, rematch.
+ rematchAllNetworksAndRequests();
+ }
+
+ private ArraySet<NetworkRequestInfo> createNrisForVpnNetworkPreference(
+ @NonNull NetworkPreferenceList<String, VpnNetworkPreferenceInfo> preferenceList) {
+ final ArraySet<NetworkRequestInfo> nris = new ArraySet<>();
+ for (VpnNetworkPreferenceInfo preferenceInfo : preferenceList) {
+ final List<NetworkRequest> requests = new ArrayList<>();
+ // Request VPN only, so other networks won't be the fallback options when VPN is not
+ // connected temporarily.
+ requests.add(createVpnRequest());
+ final Set<UidRange> uidRanges = new ArraySet(preferenceInfo.getUidRangesNoCopy());
+ setNetworkRequestUids(requests, uidRanges);
+ nris.add(new NetworkRequestInfo(Process.myUid(), requests, PREFERENCE_ORDER_VPN));
+ }
+ return nris;
+ }
+
+ /**
* Check the validity of an OEM network preference to be used for testing purposes.
* @param preference the preference to validate
* @return true if this is a valid OEM network preference test request.
diff --git a/service/src/com/android/server/connectivity/UidRangeUtils.java b/service/src/com/android/server/connectivity/UidRangeUtils.java
index 541340b..f36797d 100644
--- a/service/src/com/android/server/connectivity/UidRangeUtils.java
+++ b/service/src/com/android/server/connectivity/UidRangeUtils.java
@@ -184,4 +184,41 @@
uidRangeSet.add(new UidRange(start, stop));
return uidRangeSet;
}
+
+ private static int compare(UidRange range1, UidRange range2) {
+ return range1.start - range2.start;
+ }
+
+ /**
+ * Sort the given UidRange array.
+ *
+ * @param ranges The array of UidRange which is going to be sorted.
+ * @return Array of UidRange.
+ */
+ public static UidRange[] sortRangesByStartUid(UidRange[] ranges) {
+ final ArrayList uidRanges = new ArrayList(Arrays.asList(ranges));
+ Collections.sort(uidRanges, UidRangeUtils::compare);
+ return (UidRange[]) uidRanges.toArray(new UidRange[0]);
+ }
+
+ /**
+ * Check if the given sorted UidRange array contains overlap or not.
+ *
+ * Note that the sorted UidRange array must be sorted by increasing lower bound. If it's not,
+ * the behavior is undefined.
+ *
+ * @param ranges The sorted UidRange array which is going to be checked if there is an overlap
+ * or not.
+ * @return A boolean to indicate if the given sorted UidRange array contains overlap or not.
+ */
+ public static boolean sortedRangesContainOverlap(UidRange[] ranges) {
+ final ArrayList uidRanges = new ArrayList(Arrays.asList(ranges));
+ for (int i = 0; i + 1 < uidRanges.size(); i++) {
+ if (((UidRange) uidRanges.get(i + 1)).start <= ((UidRange) uidRanges.get(i)).stop) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/service/src/com/android/server/connectivity/VpnNetworkPreferenceInfo.java b/service/src/com/android/server/connectivity/VpnNetworkPreferenceInfo.java
new file mode 100644
index 0000000..3e111ab
--- /dev/null
+++ b/service/src/com/android/server/connectivity/VpnNetworkPreferenceInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity;
+
+import android.annotation.NonNull;
+import android.net.UidRange;
+import android.util.ArraySet;
+
+/**
+ * Record the session and UidRange for a VPN preference.
+ */
+public class VpnNetworkPreferenceInfo
+ implements NetworkPreferenceList.NetworkPreference<String> {
+
+ @NonNull
+ public final String mSession;
+
+ @NonNull
+ public final ArraySet<UidRange> mUidRanges;
+
+ public VpnNetworkPreferenceInfo(@NonNull String session,
+ @NonNull ArraySet<UidRange> uidRanges) {
+ this.mSession = session;
+ this.mUidRanges = uidRanges;
+ }
+
+ @Override
+ public boolean isCancel() {
+ return mUidRanges.isEmpty();
+ }
+
+ @Override
+ @NonNull
+ public String getKey() {
+ return mSession;
+ }
+
+ @NonNull
+ public ArraySet<UidRange> getUidRangesNoCopy() {
+ return mUidRanges;
+ }
+
+ /** toString */
+ public String toString() {
+ return "[VpnNetworkPreference session = " + mSession
+ + " uidRanges = " + mUidRanges
+ + "]";
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 8c18a89..a62ef8a 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -97,6 +97,7 @@
import android.telephony.TelephonyManager;
import android.test.MoreAsserts;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Range;
@@ -108,6 +109,7 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.RecorderCallback;
+import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.TestableNetworkCallback;
import org.junit.After;
@@ -136,6 +138,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Random;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -839,8 +842,13 @@
callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> (Objects.equals(expectUnderlyingNetworks,
- ((RecorderCallback.CallbackEntry.CapabilitiesChanged) entry)
- .getCaps().getUnderlyingNetworks())));
+ entry.getCaps().getUnderlyingNetworks())));
+ }
+
+ private void expectVpnNetwork(TestableNetworkCallback callback) {
+ callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> entry.getCaps().hasTransport(TRANSPORT_VPN));
}
@Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@@ -1622,7 +1630,7 @@
mCM.registerDefaultNetworkCallbackForUid(remoteUid, remoteUidCallback,
new Handler(Looper.getMainLooper()));
}, NETWORK_SETTINGS);
- remoteUidCallback.expectAvailableCallbacks(network);
+ remoteUidCallback.expectAvailableCallbacksWithBlockedReasonNone(network);
// The remote UDP socket can receive packets coming from the TUN interface
checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_PASS);
@@ -1666,6 +1674,56 @@
});
}
+ @Test
+ public void testSetVpnDefaultForUids() throws Exception {
+ assumeTrue(supportedHardware());
+ assumeTrue(SdkLevel.isAtLeastU());
+
+ final Network defaultNetwork = mCM.getActiveNetwork();
+ assertNotNull("There must be a default network", defaultNetwork);
+
+ final TestableNetworkCallback defaultNetworkCallback = new TestableNetworkCallback();
+ final String session = UUID.randomUUID().toString();
+ final int myUid = Process.myUid();
+
+ testAndCleanup(() -> {
+ mCM.registerDefaultNetworkCallback(defaultNetworkCallback);
+ defaultNetworkCallback.expectAvailableCallbacks(defaultNetwork);
+
+ final Range<Integer> myUidRange = new Range<>(myUid, myUid);
+ runWithShellPermissionIdentity(() -> {
+ mCM.setVpnDefaultForUids(session, List.of(myUidRange));
+ }, NETWORK_SETTINGS);
+
+ // The VPN will be the only default network for the app, so it's expected to receive
+ // onLost() callback.
+ defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
+
+ final ArrayList<Network> underlyingNetworks = new ArrayList<>();
+ underlyingNetworks.add(defaultNetwork);
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[] {"0.0.0.0/0", "::/0"} /* routes */,
+ "" /* allowedApplications */, "" /* disallowedApplications */,
+ null /* proxyInfo */, underlyingNetworks, false /* isAlwaysMetered */);
+
+ expectVpnNetwork(defaultNetworkCallback);
+ }, /* cleanup */ () -> {
+ stopVpn();
+ defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
+ }, /* cleanup */ () -> {
+ runWithShellPermissionIdentity(() -> {
+ mCM.setVpnDefaultForUids(session, new ArraySet<>());
+ }, NETWORK_SETTINGS);
+ // The default network of the app will be changed back to wifi when the VPN network
+ // preference feature is disabled.
+ defaultNetworkCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> defaultNetwork.equals(entry.getNetwork()));
+ }, /* cleanup */ () -> {
+ mCM.unregisterNetworkCallback(defaultNetworkCallback);
+ });
+ }
+
private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
final short dstPort, final short srcPort, final byte[] payload) throws IOException {
@@ -1756,7 +1814,7 @@
}
private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
- public void expectAvailableCallbacks(Network network) {
+ public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 4d90a4a..10a2821 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -120,4 +120,8 @@
public void testBlockIncomingPackets() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testBlockIncomingPackets");
}
+
+ public void testSetVpnDefaultForUids() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetVpnDefaultForUids");
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 7624f9c..0e04a80 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -233,6 +233,7 @@
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -2378,7 +2379,7 @@
}
private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
- public void expectAvailableCallbacks(Network network) {
+ public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
}
@@ -2431,7 +2432,7 @@
final List<DetailedBlockedStatusCallback> allCallbacks =
List.of(myUidCallback, otherUidCallback);
for (DetailedBlockedStatusCallback callback : allCallbacks) {
- callback.expectAvailableCallbacks(defaultNetwork);
+ callback.expectAvailableCallbacksWithBlockedReasonNone(defaultNetwork);
}
final Range<Integer> myUidRange = new Range<>(myUid, myUid);
@@ -2469,6 +2470,17 @@
runWithShellPermissionIdentity(() -> doTestBlockedStatusCallback(), NETWORK_SETTINGS);
}
+ @Test
+ public void testSetVpnDefaultForUids() {
+ assumeTrue(TestUtils.shouldTestUApis());
+ final String session = UUID.randomUUID().toString();
+ assertThrows(NullPointerException.class, () -> mCm.setVpnDefaultForUids(session, null));
+ assertThrows(SecurityException.class,
+ () -> mCm.setVpnDefaultForUids(session, new ArraySet<>()));
+ // For testing the complete behavior of setVpnDefaultForUids(), please refer to
+ // HostsideVpnTests.
+ }
+
private void doTestLegacyLockdownEnabled() throws Exception {
NetworkInfo info = mCm.getActiveNetworkInfo();
assertNotNull(info);
diff --git a/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java b/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java
index b8c552e..ad4785d 100644
--- a/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java
+++ b/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java
@@ -402,4 +402,27 @@
expected.add(uids20_24);
assertEquals(expected, UidRangeUtils.convertArrayToUidRange(input));
}
+
+ @Test
+ public void testSortRangesByStartUid() throws Exception {
+ final UidRange uid1 = new UidRange(100, 110);
+ final UidRange uid2 = new UidRange(120, 130);
+ final UidRange[] unsortedRanges = new UidRange[] {uid2, uid1};
+ final UidRange[] sortedRanges = UidRangeUtils.sortRangesByStartUid(unsortedRanges);
+ assertEquals(uid1, sortedRanges[0]);
+ assertEquals(uid2, sortedRanges[1]);
+ }
+
+ @Test
+ public void testSortedRangesContainOverlap() throws Exception {
+ final UidRange uid1 = new UidRange(100, 110);
+ final UidRange uid2 = new UidRange(109, 120);
+ final UidRange uid3 = new UidRange(120, 130);
+ final UidRange[] overlapRanges1 = new UidRange[] {uid1, uid2};
+ final UidRange[] overlapRanges2 = new UidRange[] {uid2, uid3};
+ final UidRange[] notOverlapRanges = new UidRange[] {uid1, uid3};
+ assertTrue(UidRangeUtils.sortedRangesContainOverlap(overlapRanges1));
+ assertTrue(UidRangeUtils.sortedRangesContainOverlap(overlapRanges2));
+ assertFalse(UidRangeUtils.sortedRangesContainOverlap(notOverlapRanges));
+ }
}