Merge "Add more unit tests for mobile data preferred uids feature" into sc-dev
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 f1d8c0f..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,10 +780,17 @@
}
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);
}
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/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 0d0ed17..ba61159 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -6307,8 +6307,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<>();
@@ -7451,7 +7450,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;
}
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 041a9cb..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;
@@ -117,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;
}
/**
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 77cfe23..d12726e 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -412,6 +412,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";
@@ -8478,10 +8479,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;
@@ -8506,12 +8507,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)
@@ -8524,7 +8525,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);
@@ -8532,12 +8533,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,
@@ -8568,7 +8569,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.
@@ -8590,13 +8591,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);
@@ -8619,7 +8620,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.
@@ -8629,16 +8630,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
@@ -8661,14 +8662,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.
@@ -8691,11 +8692,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(
@@ -8708,13 +8709,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);
}
@@ -9859,6 +9901,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++) {