Merge "Add unit test when querying TYPE_WIFI + null/empty/non-null subscriberId" into sc-dev
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index f469d89..b5054cf 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -158,7 +158,5 @@
sdk {
name: "tethering-module-sdk",
- java_sdk_libs: [
- "framework-tethering",
- ],
+ bootclasspath_fragments: ["com.android.tethering-bootclasspath-fragment"],
}
diff --git a/Tethering/bpf_progs/Android.bp b/Tethering/bpf_progs/Android.bp
index 289d75d..5b00dfe 100644
--- a/Tethering/bpf_progs/Android.bp
+++ b/Tethering/bpf_progs/Android.bp
@@ -48,10 +48,6 @@
"-Wall",
"-Werror",
],
- include_dirs: [
- // TODO: get rid of system/netd.
- "system/netd/bpf_progs", // for bpf_net_helpers.h
- ],
}
bpf {
@@ -61,8 +57,4 @@
"-Wall",
"-Werror",
],
- include_dirs: [
- // TODO: get rid of system/netd.
- "system/netd/bpf_progs", // for bpf_net_helpers.h
- ],
}
diff --git a/Tethering/bpf_progs/bpf_net_helpers.h b/Tethering/bpf_progs/bpf_net_helpers.h
new file mode 100644
index 0000000..c798580
--- /dev/null
+++ b/Tethering/bpf_progs/bpf_net_helpers.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/bpf.h>
+#include <linux/if_packet.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+// this returns 0 iff skb->sk is NULL
+static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
+
+static uint32_t (*bpf_get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid;
+
+static int (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data;
+
+static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
+ int len) = (void*)BPF_FUNC_skb_load_bytes;
+
+static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
+ __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
+
+static int64_t (*bpf_csum_diff)(__be32* from, __u32 from_size, __be32* to, __u32 to_size,
+ __wsum seed) = (void*)BPF_FUNC_csum_diff;
+
+static int64_t (*bpf_csum_update)(struct __sk_buff* skb, __wsum csum) = (void*)BPF_FUNC_csum_update;
+
+static int (*bpf_skb_change_proto)(struct __sk_buff* skb, __be16 proto,
+ __u64 flags) = (void*)BPF_FUNC_skb_change_proto;
+static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+ __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
+static int (*bpf_l4_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+ __u64 flags) = (void*)BPF_FUNC_l4_csum_replace;
+static int (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void*)BPF_FUNC_redirect;
+static int (*bpf_redirect_map)(const struct bpf_map_def* map, __u32 key,
+ __u64 flags) = (void*)BPF_FUNC_redirect_map;
+
+static int (*bpf_skb_change_head)(struct __sk_buff* skb, __u32 head_room,
+ __u64 flags) = (void*)BPF_FUNC_skb_change_head;
+static int (*bpf_skb_adjust_room)(struct __sk_buff* skb, __s32 len_diff, __u32 mode,
+ __u64 flags) = (void*)BPF_FUNC_skb_adjust_room;
+
+// Android only supports little endian architectures
+#define htons(x) (__builtin_constant_p(x) ? ___constant_swab16(x) : __builtin_bswap16(x))
+#define htonl(x) (__builtin_constant_p(x) ? ___constant_swab32(x) : __builtin_bswap32(x))
+#define ntohs(x) htons(x)
+#define ntohl(x) htonl(x)
+
+static inline __always_inline __unused bool is_received_skb(struct __sk_buff* skb) {
+ return skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_BROADCAST ||
+ skb->pkt_type == PACKET_MULTICAST;
+}
+
+// try to make the first 'len' header bytes readable via direct packet access
+static inline __always_inline void try_make_readable(struct __sk_buff* skb, int len) {
+ if (len > skb->len) len = skb->len;
+ if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len);
+}
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index c45ce83..3428c1d 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -1054,8 +1054,16 @@
mLastRaParams = newParams;
}
- private void logMessage(State state, int what) {
- mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
+ private void maybeLogMessage(State state, int what) {
+ switch (what) {
+ // Suppress some CMD_* to avoid log flooding.
+ case CMD_IPV6_TETHER_UPDATE:
+ case CMD_NEIGHBOR_EVENT:
+ break;
+ default:
+ mLog.log(state.getName() + " got "
+ + sMagicDecoderRing.get(what, Integer.toString(what)));
+ }
}
private void sendInterfaceState(int newInterfaceState) {
@@ -1095,7 +1103,7 @@
@Override
public boolean processMessage(Message message) {
- logMessage(this, message.what);
+ maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -1180,7 +1188,6 @@
@Override
public boolean processMessage(Message message) {
- logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
transitionTo(mInitialState);
@@ -1238,7 +1245,7 @@
public boolean processMessage(Message message) {
if (super.processMessage(message)) return true;
- logMessage(this, message.what);
+ maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
@@ -1306,7 +1313,7 @@
public boolean processMessage(Message message) {
if (super.processMessage(message)) return true;
- logMessage(this, message.what);
+ maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
@@ -1417,7 +1424,7 @@
class WaitingForRestartState extends State {
@Override
public boolean processMessage(Message message) {
- logMessage(this, message.what);
+ maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
transitionTo(mInitialState);
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 7596380..e4fbd71 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -495,11 +495,11 @@
// See NetlinkHandler.cpp: notifyInterfaceChanged.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
if (up) {
- maybeTrackNewInterfaceLocked(iface);
+ maybeTrackNewInterface(iface);
} else {
if (ifaceNameToType(iface) == TETHERING_BLUETOOTH
|| ifaceNameToType(iface) == TETHERING_WIGIG) {
- stopTrackingInterfaceLocked(iface);
+ stopTrackingInterface(iface);
} else {
// Ignore usb0 down after enabling RNDIS.
// We will handle disconnect in interfaceRemoved.
@@ -535,12 +535,12 @@
void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
- maybeTrackNewInterfaceLocked(iface);
+ maybeTrackNewInterface(iface);
}
void interfaceRemoved(String iface) {
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
- stopTrackingInterfaceLocked(iface);
+ stopTrackingInterface(iface);
}
void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
@@ -694,14 +694,14 @@
mEthernetCallback = new EthernetCallback();
mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
} else {
- stopEthernetTetheringLocked();
+ stopEthernetTethering();
}
return TETHER_ERROR_NO_ERROR;
}
- private void stopEthernetTetheringLocked() {
+ private void stopEthernetTethering() {
if (mConfiguredEthernetIface != null) {
- stopTrackingInterfaceLocked(mConfiguredEthernetIface);
+ stopTrackingInterface(mConfiguredEthernetIface);
mConfiguredEthernetIface = null;
}
if (mEthernetCallback != null) {
@@ -718,7 +718,7 @@
// Ethernet callback arrived after Ethernet tethering stopped. Ignore.
return;
}
- maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
+ maybeTrackNewInterface(iface, TETHERING_ETHERNET);
changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET));
mConfiguredEthernetIface = iface;
}
@@ -729,7 +729,7 @@
// onAvailable called after stopping Ethernet tethering.
return;
}
- stopEthernetTetheringLocked();
+ stopEthernetTethering();
}
}
@@ -1030,7 +1030,7 @@
// We can see this state on the way to both enabled and failure states.
break;
case WifiManager.WIFI_AP_STATE_ENABLED:
- enableWifiIpServingLocked(ifname, ipmode);
+ enableWifiIpServing(ifname, ipmode);
break;
case WifiManager.WIFI_AP_STATE_DISABLING:
// We can see this state on the way to disabled.
@@ -1038,7 +1038,7 @@
case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_FAILED:
default:
- disableWifiIpServingLocked(ifname, curState);
+ disableWifiIpServing(ifname, curState);
break;
}
}
@@ -1062,7 +1062,7 @@
// if no group is formed, bring it down if needed.
if (p2pInfo == null || !p2pInfo.groupFormed) {
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
+ disableWifiP2pIpServingIfNeeded(mWifiP2pTetherInterface);
mWifiP2pTetherInterface = null;
return;
}
@@ -1078,12 +1078,12 @@
mLog.w("P2P tethered interface " + mWifiP2pTetherInterface
+ "is different from current interface "
+ group.getInterface() + ", re-tether it");
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
+ disableWifiP2pIpServingIfNeeded(mWifiP2pTetherInterface);
}
// Finally bring up serving on the new interface
mWifiP2pTetherInterface = group.getInterface();
- enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY);
+ enableWifiIpServing(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY);
}
private void handleUserRestrictionAction() {
@@ -1164,7 +1164,7 @@
}
}
- private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) {
+ private void disableWifiIpServingCommon(int tetheringType, String ifname, int apState) {
mLog.log("Canceling WiFi tethering request -"
+ " type=" + tetheringType
+ " interface=" + ifname
@@ -1191,23 +1191,23 @@
: "specified interface: " + ifname));
}
- private void disableWifiIpServingLocked(String ifname, int apState) {
+ private void disableWifiIpServing(String ifname, int apState) {
// Regardless of whether we requested this transition, the AP has gone
// down. Don't try to tether again unless we're requested to do so.
// TODO: Remove this altogether, once Wi-Fi reliably gives us an
// interface name with every broadcast.
mWifiTetherRequested = false;
- disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState);
+ disableWifiIpServingCommon(TETHERING_WIFI, ifname, apState);
}
- private void disableWifiP2pIpServingLockedIfNeeded(String ifname) {
+ private void disableWifiP2pIpServingIfNeeded(String ifname) {
if (TextUtils.isEmpty(ifname)) return;
- disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* fake */ 0);
+ disableWifiIpServingCommon(TETHERING_WIFI_P2P, ifname, /* fake */ 0);
}
- private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
+ private void enableWifiIpServing(String ifname, int wifiIpMode) {
// Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
final int ipServingMode;
@@ -1224,7 +1224,7 @@
}
if (!TextUtils.isEmpty(ifname)) {
- maybeTrackNewInterfaceLocked(ifname);
+ maybeTrackNewInterface(ifname);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
@@ -2437,7 +2437,7 @@
mTetherMainSM.sendMessage(which, state, 0, newLp);
}
- private void maybeTrackNewInterfaceLocked(final String iface) {
+ private void maybeTrackNewInterface(final String iface) {
// If we don't care about this type of interface, ignore.
final int interfaceType = ifaceNameToType(iface);
if (interfaceType == TETHERING_INVALID) {
@@ -2457,10 +2457,10 @@
return;
}
- maybeTrackNewInterfaceLocked(iface, interfaceType);
+ maybeTrackNewInterface(iface, interfaceType);
}
- private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {
+ private void maybeTrackNewInterface(final String iface, int interfaceType) {
// If we have already started a TISM for this interface, skip.
if (mTetherStates.containsKey(iface)) {
mLog.log("active iface (" + iface + ") reported as added, ignoring");
@@ -2477,7 +2477,7 @@
tetherState.ipServer.start();
}
- private void stopTrackingInterfaceLocked(final String iface) {
+ private void stopTrackingInterface(final String iface) {
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index e615334..485eec8 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -398,10 +398,10 @@
// notifications (e.g. matching more than one of our callbacks).
//
// Also, it can happen that onLinkPropertiesChanged is called after
- // onLost removed the state from mNetworkMap. This appears to be due
- // to a bug in disconnectAndDestroyNetwork, which calls
- // nai.clatd.update() after the onLost callbacks.
- // TODO: fix the bug and make this method void.
+ // onLost removed the state from mNetworkMap. This is due to a bug
+ // in disconnectAndDestroyNetwork, which calls nai.clatd.update()
+ // after the onLost callbacks. This was fixed in S.
+ // TODO: make this method void when R is no longer supported.
return null;
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index 6090213..b8389ea 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -36,6 +36,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
@@ -67,10 +68,10 @@
public static final boolean BROADCAST_FIRST = false;
public static final boolean CALLBACKS_FIRST = true;
- final Map<NetworkCallback, NetworkRequestInfo> mAllCallbacks = new ArrayMap<>();
+ final Map<NetworkCallback, NetworkCallbackInfo> mAllCallbacks = new ArrayMap<>();
// This contains the callbacks tracking the system default network, whether it's registered
// with registerSystemDefaultNetworkCallback (S+) or with a custom request (R-).
- final Map<NetworkCallback, NetworkRequestInfo> mTrackingDefault = new ArrayMap<>();
+ final Map<NetworkCallback, NetworkCallbackInfo> mTrackingDefault = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mListening = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mRequested = new ArrayMap<>();
final Map<NetworkCallback, Integer> mLegacyTypeMap = new ArrayMap<>();
@@ -91,15 +92,21 @@
mContext = ctx;
}
- class NetworkRequestInfo {
- public final NetworkRequest request;
+ static class NetworkCallbackInfo {
public final Handler handler;
- NetworkRequestInfo(NetworkRequest r, Handler h) {
- request = r;
+ NetworkCallbackInfo(Handler h) {
handler = h;
}
}
+ static class NetworkRequestInfo extends NetworkCallbackInfo {
+ public final NetworkRequest request;
+ NetworkRequestInfo(NetworkRequest r, Handler h) {
+ super(h);
+ request = r;
+ }
+ }
+
boolean hasNoCallbacks() {
return mAllCallbacks.isEmpty()
&& mTrackingDefault.isEmpty()
@@ -145,7 +152,7 @@
private void sendDefaultNetworkCallbacks(TestNetworkAgent formerDefault,
TestNetworkAgent defaultNetwork) {
for (NetworkCallback cb : mTrackingDefault.keySet()) {
- final NetworkRequestInfo nri = mTrackingDefault.get(cb);
+ final NetworkCallbackInfo nri = mTrackingDefault.get(cb);
if (defaultNetwork != null) {
nri.handler.post(() -> cb.onAvailable(defaultNetwork.networkId));
nri.handler.post(() -> cb.onCapabilitiesChanged(
@@ -191,20 +198,28 @@
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
- assertFalse(mAllCallbacks.containsKey(cb));
- mAllCallbacks.put(cb, new NetworkRequestInfo(req, h));
// For R- devices, Tethering will invoke this function in 2 cases, one is to request mobile
// network, the other is to track system default network.
if (looksLikeDefaultRequest(req)) {
- assertFalse(mTrackingDefault.containsKey(cb));
- mTrackingDefault.put(cb, new NetworkRequestInfo(req, h));
+ registerSystemDefaultNetworkCallback(cb, h);
} else {
+ assertFalse(mAllCallbacks.containsKey(cb));
+ mAllCallbacks.put(cb, new NetworkRequestInfo(req, h));
assertFalse(mRequested.containsKey(cb));
mRequested.put(cb, new NetworkRequestInfo(req, h));
}
}
@Override
+ public void registerSystemDefaultNetworkCallback(
+ @NonNull NetworkCallback cb, @NonNull Handler h) {
+ assertFalse(mAllCallbacks.containsKey(cb));
+ mAllCallbacks.put(cb, new NetworkCallbackInfo(h));
+ assertFalse(mTrackingDefault.containsKey(cb));
+ mTrackingDefault.put(cb, new NetworkCallbackInfo(h));
+ }
+
+ @Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
fail("Should never be called.");
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 2b15866..c636384 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -60,6 +60,7 @@
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
@@ -779,14 +780,19 @@
}
private void verifyDefaultNetworkRequestFiled() {
- ArgumentCaptor<NetworkRequest> reqCaptor = ArgumentCaptor.forClass(NetworkRequest.class);
- verify(mCm, times(1)).requestNetwork(reqCaptor.capture(),
- any(NetworkCallback.class), any(Handler.class));
- assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue()));
+ if (isAtLeastS()) {
+ verify(mCm, times(1)).registerSystemDefaultNetworkCallback(
+ any(NetworkCallback.class), any(Handler.class));
+ } else {
+ ArgumentCaptor<NetworkRequest> reqCaptor = ArgumentCaptor.forClass(
+ NetworkRequest.class);
+ verify(mCm, times(1)).requestNetwork(reqCaptor.capture(),
+ any(NetworkCallback.class), any(Handler.class));
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue()));
+ }
+
// The default network request is only ever filed once.
verifyNoMoreInteractions(mCm);
- mUpstreamNetworkMonitor.startTrackDefaultNetwork(mEntitleMgr);
- verifyNoMoreInteractions(mCm);
}
private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index ce4ba85..173679d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE;
import static org.junit.Assert.assertEquals;
@@ -172,12 +173,17 @@
// Verify the fired default request matches expectation.
final ArgumentCaptor<NetworkRequest> requestCaptor =
ArgumentCaptor.forClass(NetworkRequest.class);
- verify(mCM, times(1)).requestNetwork(
- requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
- // For R- devices, Tethering will invoke this function in 2 cases, one is to
- // request mobile network, the other is to track system default network. Verify
- // the request is the one tracks default network.
- assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
+
+ if (isAtLeastS()) {
+ verify(mCM).registerSystemDefaultNetworkCallback(any(), any());
+ } else {
+ verify(mCM).requestNetwork(
+ requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
+ // For R- devices, Tethering will invoke this function in 2 cases, one is to
+ // request mobile network, the other is to track system default network. Verify
+ // the request is the one tracks default network.
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
+ }
mUNM.startObserveAllNetworks();
verify(mCM, times(1)).registerNetworkCallback(
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 4932952..ed9df56 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -826,6 +826,7 @@
final int originalOwnerUid = getOwnerUid();
final int[] originalAdministratorUids = getAdministratorUids();
final TransportInfo originalTransportInfo = getTransportInfo();
+ final Set<Integer> originalSubIds = getSubscriptionIds();
clearAll();
if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
// If the test network is not restricted, then it is only allowed to declare some
@@ -834,6 +835,9 @@
mTransportTypes =
(originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
| (1 << TRANSPORT_TEST);
+
+ // SubIds are only allowed for Test Networks that only declare TRANSPORT_TEST.
+ setSubscriptionIds(originalSubIds);
} else {
// If the test transport is restricted, then it may declare any transport.
mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
diff --git a/service/ServiceConnectivityResources/res/values-ky/strings.xml b/service/ServiceConnectivityResources/res/values-ky/strings.xml
index 9ff53cf..0027c8a 100644
--- a/service/ServiceConnectivityResources/res/values-ky/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Тутумдун байланыш булагы"</string>
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Системанын байланыш булагы"</string>
<string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi түйүнүнө кирүү"</string>
<string name="network_available_sign_in" msgid="2622520134876355561">"Тармакка кирүү"</string>
<!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 2ad12a6..c65246b 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -78,6 +78,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
@@ -398,6 +399,32 @@
}
/**
+ * The priority value is used when issue uid ranges rules to netd. Netd will use the priority
+ * value and uid ranges to generate corresponding ip rules specific to the given preference.
+ * Thus, any device originated data traffic of the applied uids can be routed to the altered
+ * default network which has highest priority.
+ *
+ * Note: The priority value should be in 0~1000. Larger value means lower priority, see
+ * {@link NativeUidRangeConfig}.
+ */
+ // This is default priority value for those NetworkRequests which doesn't have preference to
+ // alter default network and use the global one.
+ @VisibleForTesting
+ static final int DEFAULT_NETWORK_PRIORITY_NONE = 0;
+ // Used by automotive devices to set the network preferences used to direct traffic at an
+ // application level. See {@link #setOemNetworkPreference}.
+ @VisibleForTesting
+ static final int DEFAULT_NETWORK_PRIORITY_OEM = 10;
+ // Request that a user profile is put by default on a network matching a given preference.
+ // See {@link #setProfileNetworkPreference}.
+ @VisibleForTesting
+ static final int DEFAULT_NETWORK_PRIORITY_PROFILE = 20;
+ // Set by MOBILE_DATA_PREFERRED_UIDS setting. Use mobile data in preference even when
+ // higher-priority networks are connected.
+ @VisibleForTesting
+ static final int DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED = 30;
+
+ /**
* used internally to clear a wakelock when transitioning
* from one net to another. Clear happens when we get a new
* network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
@@ -1511,7 +1538,7 @@
}
// Note that registering observer for setting do not get initial callback when registering,
- // callers might have self-initialization to update status if need.
+ // callers must fetch the initial value of the setting themselves if needed.
private void registerSettingsCallbacks() {
// Watch for global HTTP proxy changes.
mSettingsObserver.observe(
@@ -4153,8 +4180,10 @@
final NetworkAgentInfo satisfier = nri.getSatisfier();
if (null != satisfier) {
try {
+ // TODO: Passing default network priority to netd.
mNetd.networkRemoveUidRanges(satisfier.network.getNetId(),
- toUidRangeStableParcels(nri.getUids()));
+ toUidRangeStableParcels(nri.getUids())
+ /* nri.getDefaultNetworkPriority() */);
} catch (RemoteException e) {
loge("Exception setting network preference default network", e);
}
@@ -5599,6 +5628,13 @@
// maximum limit of registered callbacks per UID.
final int mAsUid;
+ // Default network priority of this request.
+ private final int mDefaultNetworkPriority;
+
+ int getDefaultNetworkPriority() {
+ return mDefaultNetworkPriority;
+ }
+
// In order to preserve the mapping of NetworkRequest-to-callback when apps register
// callbacks using a returned NetworkRequest, the original NetworkRequest needs to be
// maintained for keying off of. This is only a concern when the original nri
@@ -5628,12 +5664,13 @@
NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r,
@Nullable final PendingIntent pi, @Nullable String callingAttributionTag) {
- this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag);
+ this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag,
+ DEFAULT_NETWORK_PRIORITY_NONE);
}
NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
@NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi,
- @Nullable String callingAttributionTag) {
+ @Nullable String callingAttributionTag, final int defaultNetworkPriority) {
ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
mNetworkRequestForCallback = requestForCallback;
@@ -5651,6 +5688,7 @@
*/
mCallbackFlags = NetworkCallback.FLAG_NONE;
mCallingAttributionTag = callingAttributionTag;
+ mDefaultNetworkPriority = defaultNetworkPriority;
}
NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m,
@@ -5680,6 +5718,7 @@
mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = callbackFlags;
mCallingAttributionTag = callingAttributionTag;
+ mDefaultNetworkPriority = DEFAULT_NETWORK_PRIORITY_NONE;
linkDeathRecipient();
}
@@ -5719,15 +5758,18 @@
mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
+ mDefaultNetworkPriority = DEFAULT_NETWORK_PRIORITY_NONE;
linkDeathRecipient();
}
NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) {
- this(asUid, Collections.singletonList(r));
+ this(asUid, Collections.singletonList(r), DEFAULT_NETWORK_PRIORITY_NONE);
}
- NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r) {
- this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */);
+ NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
+ final int defaultNetworkPriority) {
+ this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */,
+ defaultNetworkPriority);
}
// True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -6306,8 +6348,7 @@
// there may not be a strict 1:1 correlation between the two.
private final NetIdManager mNetIdManager;
- // NetworkAgentInfo keyed off its connecting messenger
- // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
+ // Tracks all NetworkAgents that are currently registered.
// NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
private final ArraySet<NetworkAgentInfo> mNetworkAgentInfos = new ArraySet<>();
@@ -7376,9 +7417,13 @@
maybeCloseSockets(nai, ranges, exemptUids);
try {
if (add) {
- mNetd.networkAddUidRanges(nai.network.netId, ranges);
+ // TODO: Passing default network priority to netd.
+ mNetd.networkAddUidRanges(nai.network.netId, ranges
+ /* DEFAULT_NETWORK_PRIORITY_NONE */);
} else {
- mNetd.networkRemoveUidRanges(nai.network.netId, ranges);
+ // TODO: Passing default network priority to netd.
+ mNetd.networkRemoveUidRanges(nai.network.netId, ranges
+ /* DEFAULT_NETWORK_PRIORITY_NONE */);
}
} catch (Exception e) {
loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges +
@@ -7450,7 +7495,7 @@
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
ensureRunningOnConnectivityServiceThread();
- if (getNetworkAgentInfoForNetId(nai.network.getNetId()) != nai) {
+ if (!mNetworkAgentInfos.contains(nai)) {
// Ignore updates for disconnected networks
return;
}
@@ -7689,14 +7734,18 @@
+ " any applications to set as the default." + nri);
}
if (null != newDefaultNetwork) {
+ // TODO: Passing default network priority to netd.
mNetd.networkAddUidRanges(
newDefaultNetwork.network.getNetId(),
- toUidRangeStableParcels(nri.getUids()));
+ toUidRangeStableParcels(nri.getUids())
+ /* nri.getDefaultNetworkPriority() */);
}
if (null != oldDefaultNetwork) {
+ // TODO: Passing default network priority to netd.
mNetd.networkRemoveUidRanges(
oldDefaultNetwork.network.getNetId(),
- toUidRangeStableParcels(nri.getUids()));
+ toUidRangeStableParcels(nri.getUids())
+ /* nri.getDefaultNetworkPriority() */);
}
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception setting app default network", e);
@@ -9194,6 +9243,12 @@
return results;
}
+ private boolean isLocationPermissionRequiredForConnectivityDiagnostics(
+ @NonNull NetworkAgentInfo nai) {
+ // TODO(b/188483916): replace with a transport-agnostic location-aware check
+ return nai.networkCapabilities.hasTransport(TRANSPORT_WIFI);
+ }
+
private boolean hasLocationPermission(String packageName, int uid) {
// LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
// and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
@@ -9236,7 +9291,8 @@
return false;
}
- return hasLocationPermission(callbackPackageName, callbackUid);
+ return !isLocationPermissionRequiredForConnectivityDiagnostics(nai)
+ || hasLocationPermission(callbackPackageName, callbackUid);
}
@Override
@@ -9747,7 +9803,8 @@
nrs.add(createDefaultInternetRequestForTransport(
TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids()));
- final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs);
+ final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs,
+ DEFAULT_NETWORK_PRIORITY_PROFILE);
result.add(nri);
}
return result;
@@ -9817,7 +9874,8 @@
ranges.add(new UidRange(uid, uid));
}
setNetworkRequestUids(requests, ranges);
- nris.add(new NetworkRequestInfo(Process.myUid(), requests));
+ nris.add(new NetworkRequestInfo(Process.myUid(), requests,
+ DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED));
return nris;
}
@@ -10127,7 +10185,8 @@
ranges.add(new UidRange(uid, uid));
}
setNetworkRequestUids(requests, ranges);
- return new NetworkRequestInfo(Process.myUid(), requests);
+ return new NetworkRequestInfo(
+ Process.myUid(), requests, DEFAULT_NETWORK_PRIORITY_OEM);
}
private NetworkRequest createUnmeteredNetworkRequest() {
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index 09873f4..fffd2be 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -48,7 +48,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
-import com.android.net.module.util.PermissionUtils;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -123,6 +122,8 @@
addr.getPrefixLength());
}
+ NetdUtils.setInterfaceUp(mNetd, iface);
+
return new TestNetworkInterface(tunIntf, iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -324,14 +325,6 @@
}
try {
- final long token = Binder.clearCallingIdentity();
- try {
- PermissionUtils.enforceNetworkStackPermission(mContext);
- NetdUtils.setInterfaceUp(mNetd, iface);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
// Synchronize all accesses to mTestNetworkTracker to prevent the case where:
// 1. TestNetworkAgent successfully binds to death of binder
// 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index 10e43e7..c54ee91 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -16,6 +16,14 @@
package android.net.cts;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_AES_GCM;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_MD5;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA1;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+
import static org.junit.Assert.assertArrayEquals;
import android.content.Context;
@@ -31,6 +39,12 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -42,12 +56,10 @@
import java.net.Socket;
import java.net.SocketException;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
public class IpSecBaseTest {
@@ -71,6 +83,18 @@
0x20, 0x21, 0x22, 0x23
};
+ private static final Set<String> MANDATORY_IPSEC_ALGOS_SINCE_P = new HashSet<>();
+
+ static {
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(CRYPT_AES_CBC);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_HMAC_MD5);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_HMAC_SHA1);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_HMAC_SHA256);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_HMAC_SHA384);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_HMAC_SHA512);
+ MANDATORY_IPSEC_ALGOS_SINCE_P.add(AUTH_CRYPT_AES_GCM);
+ }
+
protected static final byte[] AUTH_KEY = getKey(256);
protected static final byte[] CRYPT_KEY = getKey(256);
@@ -89,8 +113,24 @@
.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ /** Checks if an IPsec algorithm is enabled on the device */
+ protected static boolean hasIpSecAlgorithm(String algorithm) {
+ if (SdkLevel.isAtLeastS()) {
+ return IpSecAlgorithm.getSupportedAlgorithms().contains(algorithm);
+ } else {
+ return MANDATORY_IPSEC_ALGOS_SINCE_P.contains(algorithm);
+ }
+ }
+
+ protected static byte[] getKeyBytes(int byteLength) {
+ return Arrays.copyOf(KEY_DATA, byteLength);
+ }
+
protected static byte[] getKey(int bitLength) {
- return Arrays.copyOf(KEY_DATA, bitLength / 8);
+ if (bitLength % 8 != 0) {
+ throw new IllegalArgumentException("Invalid key length in bits" + bitLength);
+ }
+ return getKeyBytes(bitLength / 8);
}
protected static int getDomain(InetAddress address) {
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index d08f6e9..e7e1d67 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -16,10 +16,33 @@
package android.net.cts;
+import static android.net.IpSecAlgorithm.AUTH_AES_CMAC;
+import static android.net.IpSecAlgorithm.AUTH_AES_XCBC;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_AES_GCM;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_MD5;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA1;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
import static android.net.cts.PacketUtils.AES_CBC_BLK_SIZE;
import static android.net.cts.PacketUtils.AES_CBC_IV_LEN;
+import static android.net.cts.PacketUtils.AES_CMAC_ICV_LEN;
+import static android.net.cts.PacketUtils.AES_CMAC_KEY_LEN;
+import static android.net.cts.PacketUtils.AES_CTR_BLK_SIZE;
+import static android.net.cts.PacketUtils.AES_CTR_IV_LEN;
+import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN;
import static android.net.cts.PacketUtils.AES_GCM_BLK_SIZE;
import static android.net.cts.PacketUtils.AES_GCM_IV_LEN;
+import static android.net.cts.PacketUtils.AES_XCBC_ICV_LEN;
+import static android.net.cts.PacketUtils.AES_XCBC_KEY_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_BLK_SIZE;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_ICV_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_IV_LEN;
+import static android.net.cts.PacketUtils.HMAC_SHA512_ICV_LEN;
+import static android.net.cts.PacketUtils.HMAC_SHA512_KEY_LEN;
import static android.net.cts.PacketUtils.IP4_HDRLEN;
import static android.net.cts.PacketUtils.IP6_HDRLEN;
import static android.net.cts.PacketUtils.TCP_HDRLEN_WITH_TIMESTAMP_OPT;
@@ -27,15 +50,20 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.compatibility.common.util.PropertyUtil.getFirstApiLevel;
+import static com.android.compatibility.common.util.PropertyUtil.getVendorApiLevel;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.TrafficStats;
+import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.Os;
@@ -44,8 +72,11 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.SkipPresubmit;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,10 +87,15 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class IpSecManagerTest extends IpSecBaseTest {
+ @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
private static final String TAG = IpSecManagerTest.class.getSimpleName();
@@ -417,8 +453,12 @@
switch (cryptOrAead.getName()) {
case IpSecAlgorithm.CRYPT_AES_CBC:
return AES_CBC_IV_LEN;
+ case IpSecAlgorithm.CRYPT_AES_CTR:
+ return AES_CTR_IV_LEN;
case IpSecAlgorithm.AUTH_CRYPT_AES_GCM:
return AES_GCM_IV_LEN;
+ case IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305:
+ return CHACHA20_POLY1305_IV_LEN;
default:
throw new IllegalArgumentException(
"IV length unknown for algorithm" + cryptOrAead.getName());
@@ -433,8 +473,12 @@
switch (cryptOrAead.getName()) {
case IpSecAlgorithm.CRYPT_AES_CBC:
return AES_CBC_BLK_SIZE;
+ case IpSecAlgorithm.CRYPT_AES_CTR:
+ return AES_CTR_BLK_SIZE;
case IpSecAlgorithm.AUTH_CRYPT_AES_GCM:
return AES_GCM_BLK_SIZE;
+ case IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305:
+ return CHACHA20_POLY1305_BLK_SIZE;
default:
throw new IllegalArgumentException(
"Blk size unknown for algorithm" + cryptOrAead.getName());
@@ -516,7 +560,6 @@
int blkSize,
int truncLenBits)
throws Exception {
-
int innerPacketSize = TEST_DATA.length + transportHdrLen + ipHdrLen;
int outerPacketSize =
PacketUtils.calculateEspPacketSize(
@@ -663,6 +706,41 @@
// checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, true, 1000);
// }
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testGetSupportedAlgorithms() throws Exception {
+ final Map<String, Integer> algoToRequiredMinSdk = new HashMap<>();
+ algoToRequiredMinSdk.put(CRYPT_AES_CBC, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_HMAC_MD5, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_HMAC_SHA1, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_HMAC_SHA256, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_HMAC_SHA384, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_HMAC_SHA512, Build.VERSION_CODES.P);
+ algoToRequiredMinSdk.put(AUTH_CRYPT_AES_GCM, Build.VERSION_CODES.P);
+
+ // TODO: b/170424293 Use Build.VERSION_CODES.S when is finalized
+ algoToRequiredMinSdk.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
+ algoToRequiredMinSdk.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
+ algoToRequiredMinSdk.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
+ algoToRequiredMinSdk.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+
+ final Set<String> supportedAlgos = IpSecAlgorithm.getSupportedAlgorithms();
+
+ // Verify all supported algorithms are valid
+ for (String algo : supportedAlgos) {
+ assertTrue("Found invalid algo " + algo, algoToRequiredMinSdk.keySet().contains(algo));
+ }
+
+ // Verify all mandatory algorithms are supported
+ for (Entry<String, Integer> entry : algoToRequiredMinSdk.entrySet()) {
+ if (Math.min(getFirstApiLevel(), getVendorApiLevel()) >= entry.getValue()) {
+ assertTrue(
+ "Fail to support " + entry.getKey(),
+ supportedAlgos.contains(entry.getKey()));
+ }
+ }
+ }
+
@Test
public void testInterfaceCountersUdp4() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
@@ -849,6 +927,152 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ private static IpSecAlgorithm buildCryptAesCtr() throws Exception {
+ return new IpSecAlgorithm(CRYPT_AES_CTR, getKeyBytes(AES_CTR_KEY_LEN));
+ }
+
+ private static IpSecAlgorithm buildAuthHmacSha512() throws Exception {
+ return new IpSecAlgorithm(
+ AUTH_HMAC_SHA512, getKeyBytes(HMAC_SHA512_KEY_LEN), HMAC_SHA512_ICV_LEN * 8);
+ }
+
+ @Test
+ public void testAesCtrHmacSha512Tcp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
+ public void testAesCtrHmacSha512Tcp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCtrHmacSha512Udp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCtrHmacSha512Udp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ private static IpSecAlgorithm buildCryptAesCbc() throws Exception {
+ return new IpSecAlgorithm(CRYPT_AES_CBC, CRYPT_KEY);
+ }
+
+ private static IpSecAlgorithm buildAuthAesXcbc() throws Exception {
+ return new IpSecAlgorithm(
+ AUTH_AES_XCBC, getKeyBytes(AES_XCBC_KEY_LEN), AES_XCBC_ICV_LEN * 8);
+ }
+
+ private static IpSecAlgorithm buildAuthAesCmac() throws Exception {
+ return new IpSecAlgorithm(
+ AUTH_AES_CMAC, getKeyBytes(AES_CMAC_KEY_LEN), AES_CMAC_ICV_LEN * 8);
+ }
+
+ @Test
+ public void testAesCbcAesXCbcTcp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesXcbc();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
+ public void testAesCbcAesXCbcTcp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesXcbc();
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesXCbcUdp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesXcbc();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesXCbcUdp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesXcbc();
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesCmacTcp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesCmac();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
+ public void testAesCbcAesCmacTcp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesCmac();
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesCmacUdp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesCmac();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesCmacUdp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = buildCryptAesCbc();
+ final IpSecAlgorithm auth = buildAuthAesCmac();
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
@Test
public void testAesGcm64Tcp4() throws Exception {
IpSecAlgorithm authCrypt =
@@ -948,6 +1172,48 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ private static IpSecAlgorithm buildAuthCryptChaCha20Poly1305() throws Exception {
+ return new IpSecAlgorithm(
+ AUTH_CRYPT_CHACHA20_POLY1305, AEAD_KEY, CHACHA20_POLY1305_ICV_LEN * 8);
+ }
+
+ @Test
+ public void testChaCha20Poly1305Tcp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ @Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
+ public void testChaCha20Poly1305Tcp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ @Test
+ public void testChaCha20Poly1305Udp4() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ @Test
+ public void testChaCha20Poly1305Udp6() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
@Test
public void testAesCbcHmacMd5Tcp4UdpEncap() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
@@ -1029,6 +1295,66 @@
}
@Test
+ public void testAesCtrHmacSha512Tcp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
+ public void testAesCtrHmacSha512Udp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+ final IpSecAlgorithm crypt = buildCryptAesCtr();
+ final IpSecAlgorithm auth = buildAuthHmacSha512();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesXCbcTcp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = new IpSecAlgorithm(CRYPT_AES_CBC, CRYPT_KEY);
+ final IpSecAlgorithm auth = new IpSecAlgorithm(AUTH_AES_XCBC, getKey(128), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesXCbcUdp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+ final IpSecAlgorithm crypt = new IpSecAlgorithm(CRYPT_AES_CBC, CRYPT_KEY);
+ final IpSecAlgorithm auth = new IpSecAlgorithm(AUTH_AES_XCBC, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesCmacTcp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = new IpSecAlgorithm(CRYPT_AES_CBC, CRYPT_KEY);
+ final IpSecAlgorithm auth = new IpSecAlgorithm(AUTH_AES_CMAC, getKey(128), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
+ public void testAesCbcAesCmacUdp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+ final IpSecAlgorithm crypt = new IpSecAlgorithm(CRYPT_AES_CBC, CRYPT_KEY);
+ final IpSecAlgorithm auth = new IpSecAlgorithm(AUTH_AES_CMAC, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ @Test
public void testAesGcm64Tcp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -1077,6 +1403,24 @@
}
@Test
+ public void testChaCha20Poly1305Tcp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ @Test
+ public void testChaCha20Poly1305Udp4UdpEncap() throws Exception {
+ assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+ final IpSecAlgorithm authCrypt = buildAuthCryptChaCha20Poly1305();
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ @Test
public void testCryptUdp4() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, null, null, false, 1, false);
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
index 0aedecb..7e622f6 100644
--- a/tests/cts/net/src/android/net/cts/PacketUtils.java
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -43,17 +43,35 @@
static final int UDP_HDRLEN = 8;
static final int TCP_HDRLEN = 20;
static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
+ static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
// Not defined in OsConstants
static final int IPPROTO_IPV4 = 4;
static final int IPPROTO_ESP = 50;
// Encryption parameters
- static final int AES_GCM_IV_LEN = 8;
static final int AES_CBC_IV_LEN = 16;
- static final int AES_GCM_BLK_SIZE = 4;
static final int AES_CBC_BLK_SIZE = 16;
+ static final int AES_CTR_KEY_LEN = 20;
+ static final int AES_CTR_BLK_SIZE = ESP_BLK_SIZE;
+ static final int AES_CTR_IV_LEN = 8;
+
+ // AEAD parameters
+ static final int AES_GCM_IV_LEN = 8;
+ static final int AES_GCM_BLK_SIZE = 4;
+ static final int CHACHA20_POLY1305_BLK_SIZE = ESP_BLK_SIZE;
+ static final int CHACHA20_POLY1305_IV_LEN = 8;
+ static final int CHACHA20_POLY1305_ICV_LEN = 16;
+
+ // Authentication parameters
+ static final int HMAC_SHA512_KEY_LEN = 64;
+ static final int HMAC_SHA512_ICV_LEN = 32;
+ static final int AES_XCBC_KEY_LEN = 16;
+ static final int AES_XCBC_ICV_LEN = 12;
+ static final int AES_CMAC_KEY_LEN = 16;
+ static final int AES_CMAC_ICV_LEN = 12;
+
// Encryption algorithms
static final String AES = "AES";
static final String AES_CBC = "AES/CBC/NoPadding";
diff --git a/tests/cts/net/util/Android.bp b/tests/cts/net/util/Android.bp
index 88a2068..b5f1208 100644
--- a/tests/cts/net/util/Android.bp
+++ b/tests/cts/net/util/Android.bp
@@ -26,5 +26,6 @@
"compatibility-device-util-axt",
"junit",
"net-tests-utils",
+ "modules-utils-build",
],
}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index d5a26c4..b32218b 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -23,6 +23,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION;
+import static com.android.compatibility.common.util.PropertyUtil.getFirstApiLevel;
import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
@@ -55,7 +56,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
-import android.os.SystemProperties;
import android.provider.Settings;
import android.system.Os;
import android.system.OsConstants;
@@ -87,6 +87,7 @@
private static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 10_000;
private static final int CONNECTIVITY_CHANGE_TIMEOUT_SECS = 30;
private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ private static final String PRIVATE_DNS_MODE_STRICT = "hostname";
public static final int HTTP_PORT = 80;
public static final String TEST_HOST = "connectivitycheck.gstatic.com";
public static final String HTTP_REQUEST =
@@ -116,8 +117,7 @@
/** Checks if FEATURE_IPSEC_TUNNELS is enabled on the device */
public boolean hasIpsecTunnelsFeature() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
- || SystemProperties.getInt("ro.product.first_api_level", 0)
- >= Build.VERSION_CODES.Q;
+ || getFirstApiLevel() >= Build.VERSION_CODES.Q;
}
/**
@@ -524,12 +524,17 @@
}
public void restorePrivateDnsSetting() throws InterruptedException {
- if (mOldPrivateDnsMode == null || mOldPrivateDnsSpecifier == null) {
- return;
+ if (mOldPrivateDnsMode == null) {
+ fail("restorePrivateDnsSetting without storing settings first");
}
// restore private DNS setting
- if ("hostname".equals(mOldPrivateDnsMode)) {
+ if (PRIVATE_DNS_MODE_STRICT.equals(mOldPrivateDnsMode)) {
setPrivateDnsStrictMode(mOldPrivateDnsSpecifier);
+
+ // In case of invalid setting, still restore it but fail the test
+ if (mOldPrivateDnsSpecifier == null) {
+ fail("Invalid private DNS setting: no hostname specified in strict mode");
+ }
awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
mCm.getActiveNetwork(),
mOldPrivateDnsSpecifier, true);
@@ -540,13 +545,14 @@
public void setPrivateDnsStrictMode(String server) {
// To reduce flake rate, set PRIVATE_DNS_SPECIFIER before PRIVATE_DNS_MODE. This ensures
- // that if the previous private DNS mode was not "hostname", the system only sees one
+ // that if the previous private DNS mode was not strict, the system only sees one
// EVENT_PRIVATE_DNS_SETTINGS_CHANGED event instead of two.
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, server);
final String mode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
- // If current private DNS mode is "hostname", we only need to set PRIVATE_DNS_SPECIFIER.
- if (!"hostname".equals(mode)) {
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+ // If current private DNS mode is strict, we only need to set PRIVATE_DNS_SPECIFIER.
+ if (!PRIVATE_DNS_MODE_STRICT.equals(mode)) {
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE,
+ PRIVATE_DNS_MODE_STRICT);
}
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index d20a089..684872a 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -118,6 +118,8 @@
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
+import static com.android.server.ConnectivityService.DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED;
+import static com.android.server.ConnectivityService.DEFAULT_NETWORK_PRIORITY_OEM;
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.testutils.ConcurrentUtils.await;
import static com.android.testutils.ConcurrentUtils.durationOf;
@@ -412,6 +414,7 @@
UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
+ private static final String CLAT_MOBILE_IFNAME = CLAT_PREFIX + MOBILE_IFNAME;
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
private static final String VPN_IFNAME = "tun10042";
@@ -455,6 +458,7 @@
private TestNetworkCallback mDefaultNetworkCallback;
private TestNetworkCallback mSystemDefaultNetworkCallback;
private TestNetworkCallback mProfileDefaultNetworkCallback;
+ private TestNetworkCallback mTestPackageDefaultNetworkCallback;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mBlockedReasons = BLOCKED_REASON_NONE;
@@ -8477,10 +8481,10 @@
private LinkProperties makeClatLinkProperties(LinkAddress la) {
LinkAddress clatAddress = la;
LinkProperties stacked = new LinkProperties();
- stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME);
+ stacked.setInterfaceName(CLAT_MOBILE_IFNAME);
RouteInfo ipv4Default = new RouteInfo(
new LinkAddress(Inet4Address.ANY, 0),
- clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME);
+ clatAddress.getAddress(), CLAT_MOBILE_IFNAME);
stacked.addRoute(ipv4Default);
stacked.addLinkAddress(clatAddress);
return stacked;
@@ -8505,12 +8509,12 @@
final String kOtherNat64PrefixString = "64:ff9b::";
final IpPrefix kOtherNat64Prefix = new IpPrefix(
InetAddress.getByName(kOtherNat64PrefixString), 96);
- final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
- MOBILE_IFNAME);
+ final RouteInfo ipv6Default =
+ new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME);
final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
final RouteInfo ipv4Subnet = new RouteInfo(myIpv4, null, MOBILE_IFNAME);
- final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(),
- CLAT_PREFIX + MOBILE_IFNAME);
+ final RouteInfo stackedDefault =
+ new RouteInfo((IpPrefix) null, myIpv4.getAddress(), CLAT_MOBILE_IFNAME);
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
@@ -8523,7 +8527,7 @@
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
cellLp.addLinkAddress(myIpv6);
- cellLp.addRoute(defaultRoute);
+ cellLp.addRoute(ipv6Default);
cellLp.addRoute(ipv6Subnet);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mMockDnsResolver);
@@ -8531,12 +8535,12 @@
// Connect with ipv6 link properties. Expect prefix discovery to be started.
mCellNetworkAgent.connect(true);
- final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ int cellNetId = mCellNetworkAgent.getNetwork().netId;
waitForIdle();
verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
INetd.PERMISSION_NONE));
- assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
+ assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
@@ -8567,7 +8571,7 @@
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+ when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfigParcel(myIpv4));
// Remove IPv4 address. Expect prefix discovery to be started again.
@@ -8589,13 +8593,13 @@
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
- clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
.getStackedLinks();
assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
assertRoutesAdded(cellNetId, stackedDefault);
- verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
// Change trivial linkproperties and see if stacked link is preserved.
cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
@@ -8618,7 +8622,7 @@
new int[] { TRANSPORT_CELLULAR });
}
reset(mMockNetd);
- when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+ when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfigParcel(myIpv4));
// Change the NAT64 prefix without first removing it.
// Expect clatd to be stopped and started with the new prefix.
@@ -8628,16 +8632,16 @@
(lp) -> lp.getStackedLinks().size() == 0);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
assertRoutesRemoved(cellNetId, stackedDefault);
- verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
- clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 1);
assertRoutesAdded(cellNetId, stackedDefault);
- verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
reset(mMockNetd);
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
@@ -8660,14 +8664,14 @@
assertRoutesRemoved(cellNetId, stackedDefault);
// The interface removed callback happens but has no effect after stop is called.
- clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+ clat.interfaceRemoved(CLAT_MOBILE_IFNAME);
networkCallback.assertNoCallback();
- verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
+ when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfigParcel(myIpv4));
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
@@ -8690,11 +8694,11 @@
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
- clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null);
assertRoutesAdded(cellNetId, stackedDefault);
- verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
// NAT64 prefix is removed. Expect that clat is stopped.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
@@ -8707,13 +8711,54 @@
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0);
- verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
- verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME);
- verifyNoMoreInteractions(mMockNetd);
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Clean up.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
+ verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
+ verify(mMockNetd).networkDestroy(cellNetId);
+ verifyNoMoreInteractions(mMockNetd);
+ reset(mMockNetd);
+
+ // Test disconnecting a network that is running 464xlat.
+
+ // Connect a network with a NAT64 prefix.
+ when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ cellLp.setNat64Prefix(kNat64Prefix);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(false /* validated */);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ cellNetId = mCellNetworkAgent.getNetwork().netId;
+ verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
+ INetd.PERMISSION_NONE));
+ assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
+
+ // Clatd is started and clat iface comes up. Expect stacked link to be added.
+ verify(mMockNetd).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+ clat = getNat464Xlat(mCellNetworkAgent);
+ clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true /* up */);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 1
+ && lp.getNat64Prefix().equals(kNat64Prefix));
+ verify(mMockNetd).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
+ // assertRoutesAdded sees all calls since last mMockNetd reset, so expect IPv6 routes again.
+ assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default, stackedDefault);
+ reset(mMockNetd);
+
+ // Disconnect the network. clat is stopped and the network is destroyed.
+ mCellNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ networkCallback.assertNoCallback();
+ verify(mMockNetd).clatdStop(MOBILE_IFNAME);
+ verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
+ verify(mMockNetd).networkDestroy(cellNetId);
+ verifyNoMoreInteractions(mMockNetd);
+
mCm.unregisterNetworkCallback(networkCallback);
}
@@ -9858,6 +9903,8 @@
}
private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
+ // TODO: add @JavaDerive(equals=true) to RouteInfoParcel, use eq() directly, and delete
+ // assertRouteInfoParcelMatches above.
ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
for (int i = 0; i < routes.length; i++) {
@@ -9924,10 +9971,24 @@
}
public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) {
+ final NetworkCapabilities cellNc = new NetworkCapabilities.Builder(nc)
+ .addTransportType(TRANSPORT_CELLULAR).build();
final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE,
ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
- return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
+ return fakeNai(cellNc, info);
+ }
+
+ private NetworkAgentInfo fakeWifiNai(NetworkCapabilities nc) {
+ final NetworkCapabilities wifiNc = new NetworkCapabilities.Builder(nc)
+ .addTransportType(TRANSPORT_WIFI).build();
+ final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0 /* subtype */,
+ ConnectivityManager.getNetworkTypeName(TYPE_WIFI), "" /* subtypeName */);
+ return fakeNai(wifiNc, info);
+ }
+
+ private NetworkAgentInfo fakeNai(NetworkCapabilities nc, NetworkInfo networkInfo) {
+ return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker,
@@ -9953,7 +10014,7 @@
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setAdministratorUids(new int[] {wrongUid});
- final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
+ final NetworkAgentInfo naiWithUid = fakeWifiNai(nc);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
@@ -9963,18 +10024,37 @@
Process.myPid() + 1, wrongUid, naiWithUid, mContext.getOpPackageName()));
}
+ private void verifyConnectivityDiagnosticsPermissionsWithNetworkAgentInfo(
+ NetworkAgentInfo info, boolean expectPermission) {
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ assertEquals(
+ "Unexpected ConnDiags permission",
+ expectPermission,
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), info, mContext.getOpPackageName()));
+ }
+
@Test
- public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
+ public void testCheckConnectivityDiagnosticsPermissionsCellularNoLocationPermission()
+ throws Exception {
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setAdministratorUids(new int[] {Process.myUid()});
final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
- mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+ verifyConnectivityDiagnosticsPermissionsWithNetworkAgentInfo(naiWithUid,
+ true /* expectPermission */);
+ }
- assertFalse(
- "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
- mService.checkConnectivityDiagnosticsPermissions(
- Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsWifiNoLocationPermission()
+ throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setAdministratorUids(new int[] {Process.myUid()});
+ final NetworkAgentInfo naiWithUid = fakeWifiNai(nc);
+
+ verifyConnectivityDiagnosticsPermissionsWithNetworkAgentInfo(naiWithUid,
+ false /* expectPermission */);
}
@Test
@@ -10659,8 +10739,9 @@
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
-
- final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ final NetworkRequestInfo nri = nris.iterator().next();
+ assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+ final List<NetworkRequest> mRequests = nri.mRequests;
assertEquals(expectedNumOfNris, nris.size());
assertEquals(expectedNumOfRequests, mRequests.size());
assertTrue(mRequests.get(0).isListen());
@@ -10688,8 +10769,9 @@
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
-
- final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ final NetworkRequestInfo nri = nris.iterator().next();
+ assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+ final List<NetworkRequest> mRequests = nri.mRequests;
assertEquals(expectedNumOfNris, nris.size());
assertEquals(expectedNumOfRequests, mRequests.size());
assertTrue(mRequests.get(0).isListen());
@@ -10714,8 +10796,9 @@
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
-
- final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ final NetworkRequestInfo nri = nris.iterator().next();
+ assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+ final List<NetworkRequest> mRequests = nri.mRequests;
assertEquals(expectedNumOfNris, nris.size());
assertEquals(expectedNumOfRequests, mRequests.size());
assertTrue(mRequests.get(0).isRequest());
@@ -10737,8 +10820,9 @@
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
-
- final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ final NetworkRequestInfo nri = nris.iterator().next();
+ assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+ final List<NetworkRequest> mRequests = nri.mRequests;
assertEquals(expectedNumOfNris, nris.size());
assertEquals(expectedNumOfRequests, mRequests.size());
assertTrue(mRequests.get(0).isRequest());
@@ -11002,16 +11086,24 @@
}
private void registerDefaultNetworkCallbacks() {
+ if (mSystemDefaultNetworkCallback != null || mDefaultNetworkCallback != null
+ || mProfileDefaultNetworkCallback != null
+ || mTestPackageDefaultNetworkCallback != null) {
+ throw new IllegalStateException("Default network callbacks already registered");
+ }
+
// Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback()
mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
mSystemDefaultNetworkCallback = new TestNetworkCallback();
mDefaultNetworkCallback = new TestNetworkCallback();
mProfileDefaultNetworkCallback = new TestNetworkCallback();
+ mTestPackageDefaultNetworkCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
TEST_WORK_PROFILE_APP_UID);
+ registerDefaultNetworkCallbackAsUid(mTestPackageDefaultNetworkCallback, TEST_PACKAGE_UID);
// TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well.
mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED);
}
@@ -11026,6 +11118,9 @@
if (null != mProfileDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
}
+ if (null != mTestPackageDefaultNetworkCallback) {
+ mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -12915,6 +13010,8 @@
assertEquals(1, nris.size());
assertTrue(nri.isMultilayerRequest());
assertEquals(nri.getUids(), uidRangesForUids(uids));
+ assertEquals(DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED,
+ nri.getDefaultNetworkPriority());
}
/**
@@ -12947,9 +13044,11 @@
@Test
public void testMobileDataPreferredUidsChanged() throws Exception {
final InOrder inorder = inOrder(mMockNetd);
+ registerDefaultNetworkCallbacks();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- waitForIdle();
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
final int cellNetId = mCellNetworkAgent.getNetwork().netId;
inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
@@ -12960,12 +13059,15 @@
inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+ // Set MOBILE_DATA_PREFERRED_UIDS setting and verify that net id and uid ranges send to netd
final Set<Integer> uids1 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
final UidRangeParcel[] uidRanges1 = toUidRangeStableParcels(uidRangesForUids(uids1));
setAndUpdateMobileDataPreferredUids(uids1);
inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges1);
inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+ // Set MOBILE_DATA_PREFERRED_UIDS setting again and verify that old rules are removed and
+ // new rules are added.
final Set<Integer> uids2 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID),
PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2),
SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
@@ -12973,5 +13075,170 @@
setAndUpdateMobileDataPreferredUids(uids2);
inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(cellNetId, uidRanges1);
inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges2);
+
+ // Clear MOBILE_DATA_PREFERRED_UIDS setting again and verify that old rules are removed and
+ // new rules are not added.
+ final Set<Integer> uids3 = Set.of();
+ final UidRangeParcel[] uidRanges3 = toUidRangeStableParcels(uidRangesForUids(uids3));
+ setAndUpdateMobileDataPreferredUids(uids3);
+ inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(cellNetId, uidRanges2);
+ inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+ }
+
+ /**
+ * Make sure mobile data preferred uids feature behaves as expected when the mobile network
+ * goes up and down while the uids is set. Make sure they behave as expected whether
+ * there is a general default network or not.
+ */
+ @Test
+ public void testMobileDataPreferenceForMobileNetworkUpDown() throws Exception {
+ final InOrder inorder = inOrder(mMockNetd);
+ // File a request for cell to ensure it doesn't go down.
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+ cellNetworkCallback.assertNoCallback();
+
+ registerDefaultNetworkCallbacks();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+ final int wifiNetId = mWiFiNetworkAgent.getNetwork().netId;
+ inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+ wifiNetId, INetd.PERMISSION_NONE));
+
+ // Initial mobile data preferred uids status.
+ setAndUpdateMobileDataPreferredUids(Set.of());
+ inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+ // Set MOBILE_DATA_PREFERRED_UIDS setting and verify that wifi net id and uid ranges send to
+ // netd.
+ final Set<Integer> uids = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
+ final UidRangeParcel[] uidRanges = toUidRangeStableParcels(uidRangesForUids(uids));
+ setAndUpdateMobileDataPreferredUids(uids);
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(wifiNetId, uidRanges);
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+ // Cellular network connected. mTestPackageDefaultNetworkCallback should receive
+ // callback with cellular network and net id and uid ranges should be updated to netd.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.assertNoCallback();
+ mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+ cellNetId, INetd.PERMISSION_NONE));
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges);
+ inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(wifiNetId, uidRanges);
+
+ // Cellular network disconnected. mTestPackageDefaultNetworkCallback should receive
+ // callback with wifi network from fallback request.
+ mCellNetworkAgent.disconnect();
+ mDefaultNetworkCallback.assertNoCallback();
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mTestPackageDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mTestPackageDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(wifiNetId, uidRanges);
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+ inorder.verify(mMockNetd).networkDestroy(cellNetId);
+
+ // Cellular network comes back. mTestPackageDefaultNetworkCallback should receive
+ // callback with cellular network.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.assertNoCallback();
+ mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+ final int cellNetId2 = mCellNetworkAgent.getNetwork().netId;
+ inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+ cellNetId2, INetd.PERMISSION_NONE));
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId2, uidRanges);
+ inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(wifiNetId, uidRanges);
+
+ // Wifi network disconnected. mTestPackageDefaultNetworkCallback should not receive
+ // any callback.
+ mWiFiNetworkAgent.disconnect();
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mTestPackageDefaultNetworkCallback.assertNoCallback();
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+ waitForIdle();
+ inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+ inorder.verify(mMockNetd).networkDestroy(wifiNetId);
+
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ }
+
+ @Test
+ public void testSetMobileDataPreferredUids_noIssueToFactory() throws Exception {
+ // First set mobile data preferred uid to create a multi-layer requests: 1. listen for
+ // cellular, 2. track the default network for fallback.
+ setAndUpdateMobileDataPreferredUids(
+ Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
+
+ final HandlerThread handlerThread = new HandlerThread("MockFactory");
+ handlerThread.start();
+ NetworkCapabilities internetFilter = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "internetFactory", internetFilter, mCsHandlerThread);
+ internetFactory.setScoreFilter(40);
+
+ try {
+ internetFactory.register();
+ // Default internet request only. The first request is listen for cellular network,
+ // which is never sent to factories (it's a LISTEN, not requestable). The second
+ // fallback request is TRACK_DEFAULT which is also not sent to factories.
+ internetFactory.expectRequestAdds(1);
+ internetFactory.assertRequestCountEquals(1);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ // The internet factory however is outscored, and should lose its requests.
+ internetFactory.expectRequestRemove();
+ internetFactory.assertRequestCountEquals(0);
+
+ mCellNetworkAgent.disconnect();
+ // The network satisfying the default internet request has disconnected, so the
+ // internetFactory sees the default request again.
+ internetFactory.expectRequestAdds(1);
+ internetFactory.assertRequestCountEquals(1);
+ } finally {
+ internetFactory.terminate();
+ handlerThread.quitSafely();
+ }
+ }
+
+ /**
+ * Validate request counts are counted accurately on MOBILE_DATA_PREFERRED_UIDS change
+ * on set/replace.
+ */
+ @Test
+ public void testMobileDataPreferredUidsChangedCountsRequestsCorrectlyOnSet() throws Exception {
+ ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext,
+ Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
+ testRequestCountLimits(() -> {
+ // Set initially to test the limit prior to having existing requests.
+ mService.updateMobileDataPreferredUids();
+ waitForIdle();
+
+ // re-set so as to test the limit as part of replacing existing requests.
+ mService.updateMobileDataPreferredUids();
+ waitForIdle();
+ });
}
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index a90fa68..20be5f4 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -57,6 +57,7 @@
public class NsdServiceTest {
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
+ private static final long CLEANUP_DELAY_MS = 500;
long mTimeoutMs = 100; // non-final so that tests can adjust the value.
@@ -86,20 +87,27 @@
}
@Test
- public void testClientsCanConnectAndDisconnect() {
+ public void testNoDaemonStartedWhenClientsConnect() {
when(mSettings.isEnabled()).thenReturn(true);
NsdService service = makeService();
+ // Creating an NsdManager will not cause any cmds executed, which means
+ // no daemon is started.
NsdManager client1 = connectClient(service);
- verify(mDaemon, timeout(100).times(1)).start();
+ verify(mDaemon, never()).execute(any());
+ // Creating another NsdManager will not cause any cmds executed.
NsdManager client2 = connectClient(service);
+ verify(mDaemon, never()).execute(any());
client1.disconnect();
- client2.disconnect();
+ // Still 1 client remains, daemon shouldn't be stopped.
+ verify(mDaemon, never()).maybeStop();
- verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
+ client2.disconnect();
+ // All clients are disconnected, stop the daemon after CLEANUP_DELAY_MS.
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
client1.disconnect();
client2.disconnect();
@@ -113,7 +121,8 @@
NsdService service = makeService();
NsdManager client = connectClient(service);
- verify(mDaemon, timeout(100).times(1)).start();
+ verify(mDaemon, never()).maybeStart();
+ verify(mDaemon, never()).execute(any());
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
request.setPort(2201);
@@ -121,21 +130,24 @@
// Client registration request
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
+ verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
verifyDaemonCommand("register 2 a_name a_type 2201");
// Client discovery request
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
client.discoverServices("a_type", PROTOCOL, listener2);
+ verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
verifyDaemonCommand("discover 3 a_type");
// Client resolve request
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
client.resolveService(request, listener3);
+ verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
verifyDaemonCommand("resolve 4 a_name a_type local.");
- // Client disconnects
+ // Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
client.disconnect();
- verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned
verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
@@ -143,12 +155,38 @@
client.disconnect();
}
+ @Test
+ public void testCleanupDelayNoRequestActive() {
+ when(mSettings.isEnabled()).thenReturn(true);
+ when(mDaemon.execute(any())).thenReturn(true);
+
+ NsdService service = makeService();
+ NsdManager client = connectClient(service);
+
+ NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
+ request.setPort(2201);
+ NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
+ client.registerService(request, PROTOCOL, listener1);
+ verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
+ verifyDaemonCommand("register 2 a_name a_type 2201");
+
+ client.unregisterService(listener1);
+ verifyDaemonCommand("stop-register 2");
+
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ reset(mDaemon);
+ client.disconnect();
+ // Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ }
+
NsdService makeService() {
DaemonConnectionSupplier supplier = (callback) -> {
mDaemonCallback = callback;
return mDaemon;
};
- NsdService service = new NsdService(mContext, mSettings, mHandler, supplier);
+ NsdService service = new NsdService(mContext, mSettings,
+ mHandler, supplier, CLEANUP_DELAY_MS);
verify(mDaemon, never()).execute(any(String.class));
return service;
}
@@ -157,6 +195,13 @@
return new NsdManager(mContext, service);
}
+ void verifyDelayMaybeStopDaemon(long cleanupDelayMs) {
+ // Stop daemon shouldn't be called immediately.
+ verify(mDaemon, timeout(mTimeoutMs).times(0)).maybeStop();
+ // Clean up the daemon after CLEANUP_DELAY_MS.
+ verify(mDaemon, timeout(cleanupDelayMs + mTimeoutMs)).maybeStop();
+ }
+
void verifyDaemonCommands(String... wants) {
verifyDaemonCommand(String.join(" ", wants), wants.length);
}