Merge "Do not process null network packets on all clients"
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
index 44b3364..1e0adef 100644
--- a/Cronet/tests/cts/Android.bp
+++ b/Cronet/tests/cts/Android.bp
@@ -53,7 +53,9 @@
defaults: [
"cts_defaults",
],
+ enforce_default_target_sdk_version: true,
sdk_version: "test_current",
+ min_sdk_version: "30",
static_libs: ["CtsNetHttpTestsLib"],
// Tag this as a cts test artifact
test_suites: [
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index 93564e4..4e4251c 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -34,25 +34,13 @@
"--output $(out)",
}
+// Library to be used in coverage tests. cronet_java_tests can't be used directly because the common
+// tests need to inherit the NetHttpTests manifest.
android_library {
name: "NetHttpTestsLibPreJarJar",
- srcs: [":cronet_aml_javatests_sources"],
+ static_libs: ["cronet_java_tests"],
sdk_version: "module_current",
min_sdk_version: "30",
- static_libs: [
- "cronet_testserver_utils",
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "junit",
- "truth",
- ],
- libs: [
- "android.test.base",
- "framework-connectivity-pre-jarjar",
- // android.net.TrafficStats apis
- "framework-connectivity-t",
- ],
- lint: { test: true }
}
android_test {
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
index cf3a017..a3e86de 100644
--- a/Cronet/tests/mts/jarjar_excludes.txt
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -5,6 +5,8 @@
# cronet_tests.so is not jarjared and uses base classes. We can remove this when there's a
# separate java base target to depend on.
org\.chromium\.base\..+
+J\.cronet_tests_N(\$.+)?
+
# Do not jarjar the tests and its utils as they also do JNI with cronet_tests.so
org\.chromium\.net\..*Test.*(\$.+)?
org\.chromium\.net\.NativeTestServer(\$.+)?
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 65ea8e5..6affb62 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -17,6 +17,8 @@
package android.net.ip;
import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
@@ -405,8 +407,8 @@
/** Internals. */
- private boolean startIPv4() {
- return configureIPv4(true);
+ private boolean startIPv4(int scope) {
+ return configureIPv4(true, scope);
}
/**
@@ -616,7 +618,7 @@
}
private void stopIPv4() {
- configureIPv4(false);
+ configureIPv4(false /* enabled */, CONNECTIVITY_SCOPE_GLOBAL /* not used */);
// NOTE: All of configureIPv4() will be refactored out of existence
// into calls to InterfaceController, shared with startIPv4().
mInterfaceCtrl.clearIPv4Address();
@@ -627,11 +629,11 @@
mStaticIpv4ClientAddr = null;
}
- private boolean configureIPv4(boolean enabled) {
+ private boolean configureIPv4(boolean enabled, int scope) {
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
if (enabled) {
- mIpv4Address = requestIpv4Address(true /* useLastAddress */);
+ mIpv4Address = requestIpv4Address(scope, true /* useLastAddress */);
}
if (mIpv4Address == null) {
@@ -679,12 +681,12 @@
return (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) && !SdkLevel.isAtLeastT();
}
- private LinkAddress requestIpv4Address(final boolean useLastAddress) {
+ private LinkAddress requestIpv4Address(final int scope, final boolean useLastAddress) {
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
if (shouldNotConfigureBluetoothInterface()) return new LinkAddress(BLUETOOTH_IFACE_ADDR);
- return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
+ return mPrivateAddressCoordinator.requestDownstreamAddress(this, scope, useLastAddress);
}
private boolean startIPv6() {
@@ -998,67 +1000,6 @@
}
}
- private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- if (!currentPrefix.contains(mIpv4Address.getAddress())
- || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
- Log.e(TAG, "Invalid prefix: " + currentPrefix);
- return;
- }
-
- final LinkAddress deprecatedLinkAddress = mIpv4Address;
- mIpv4Address = requestIpv4Address(false);
- if (mIpv4Address == null) {
- mLog.e("Fail to request a new downstream prefix");
- return;
- }
- final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
-
- // Add new IPv4 address on the interface.
- if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
- mLog.e("Failed to add new IP " + srvAddr);
- return;
- }
-
- // Remove deprecated routes from local network.
- removeRoutesFromLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
- mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
-
- // Add new routes to local network.
- addRoutesToLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
- mLinkProperties.addLinkAddress(mIpv4Address);
-
- // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
- // listen on the interface configured with new IPv4 address, that results DNS validation
- // failure of downstream client even if appropriate routes have been configured.
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- return;
- }
- sendLinkProperties();
-
- // Notify DHCP server that new prefix/route has been applied on IpServer.
- final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
- (Inet4Address) mStaticIpv4ClientAddr.getAddress();
- final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
- srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
- try {
- mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error updating DHCP serving params: " + statusCode);
- }
- }
- });
- } catch (RemoteException e) {
- mLog.e("Error updating DHCP serving params", e);
- }
- }
-
private byte getHopLimit(String upstreamIface, int adjustTTL) {
try {
int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -1173,12 +1114,37 @@
mBpfCoordinator.stopMonitoring(this);
}
- class BaseServingState extends State {
+ abstract class BaseServingState extends State {
+ private final int mDesiredInterfaceState;
+
+ BaseServingState(int interfaceState) {
+ mDesiredInterfaceState = interfaceState;
+ }
+
@Override
public void enter() {
startConntrackMonitoring();
- if (!startIPv4()) {
+ startServingInterface();
+
+ if (mLastError != TETHER_ERROR_NO_ERROR) {
+ transitionTo(mInitialState);
+ }
+
+ if (DBG) Log.d(TAG, getStateString(mDesiredInterfaceState) + " serve " + mIfaceName);
+ sendInterfaceState(mDesiredInterfaceState);
+ }
+
+ private int getScope() {
+ if (mDesiredInterfaceState == STATE_TETHERED) {
+ return CONNECTIVITY_SCOPE_GLOBAL;
+ }
+
+ return CONNECTIVITY_SCOPE_LOCAL;
+ }
+
+ private void startServingInterface() {
+ if (!startIPv4(getScope())) {
mLastError = TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -1257,6 +1223,67 @@
}
return true;
}
+
+ private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+ if (!currentPrefix.contains(mIpv4Address.getAddress())
+ || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
+ Log.e(TAG, "Invalid prefix: " + currentPrefix);
+ return;
+ }
+
+ final LinkAddress deprecatedLinkAddress = mIpv4Address;
+ mIpv4Address = requestIpv4Address(getScope(), false);
+ if (mIpv4Address == null) {
+ mLog.e("Fail to request a new downstream prefix");
+ return;
+ }
+ final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
+
+ // Add new IPv4 address on the interface.
+ if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
+ mLog.e("Failed to add new IP " + srvAddr);
+ return;
+ }
+
+ // Remove deprecated routes from local network.
+ removeRoutesFromLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
+ mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
+
+ // Add new routes to local network.
+ addRoutesToLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
+ mLinkProperties.addLinkAddress(mIpv4Address);
+
+ // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
+ // listen on the interface configured with new IPv4 address, that results DNS validation
+ // failure of downstream client even if appropriate routes have been configured.
+ try {
+ mNetd.tetherApplyDnsInterfaces();
+ } catch (ServiceSpecificException | RemoteException e) {
+ mLog.e("Failed to update local DNS caching server");
+ return;
+ }
+ sendLinkProperties();
+
+ // Notify DHCP server that new prefix/route has been applied on IpServer.
+ final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
+ (Inet4Address) mStaticIpv4ClientAddr.getAddress();
+ final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
+ srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
+ try {
+ mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int statusCode) {
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error updating DHCP serving params: " + statusCode);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ mLog.e("Error updating DHCP serving params", e);
+ }
+ }
}
// Handling errors in BaseServingState.enter() by transitioning is
@@ -1265,15 +1292,8 @@
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of IpServer.
class LocalHotspotState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(STATE_LOCAL_ONLY);
+ LocalHotspotState() {
+ super(STATE_LOCAL_ONLY);
}
@Override
@@ -1301,15 +1321,8 @@
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of IpServer.
class TetheredState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(STATE_TETHERED);
+ TetheredState() {
+ super(STATE_TETHERED);
}
@Override
diff --git a/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
index 8a96988..5b19c4e 100644
--- a/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
+++ b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
@@ -29,6 +29,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -49,6 +51,8 @@
@NonNull
private List<WifiClient> mLastWifiClients = Collections.emptyList();
@NonNull
+ private List<WifiClient> mLastLocalOnlyClients = Collections.emptyList();
+ @NonNull
private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
@VisibleForTesting
@@ -72,25 +76,44 @@
*
* <p>The new list can be obtained through {@link #getLastTetheredClients()}.
* @param ipServers The IpServers used to assign addresses to clients.
- * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
- * update.
+ * @param wifiClients The list of L2-connected WiFi clients that are connected to a global
+ * hotspot. Null for no change since last update.
+ * @param localOnlyClients The list of L2-connected WiFi clients that are connected to localOnly
+ * hotspot. Null for no change since last update.
* @return True if the list of clients changed since the last calculation.
*/
public boolean updateConnectedClients(
- Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
+ Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients,
+ @Nullable List<WifiClient> localOnlyClients) {
final long now = mClock.elapsedRealtime();
- if (wifiClients != null) {
- mLastWifiClients = wifiClients;
- }
+ if (wifiClients != null) mLastWifiClients = wifiClients;
+ if (localOnlyClients != null) mLastLocalOnlyClients = localOnlyClients;
+
final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
+ final Set<MacAddress> localOnlyClientMacs = getClientMacs(mLastLocalOnlyClients);
// Build the list of non-expired leases from all IpServers, grouped by mac address
final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
for (IpServer server : ipServers) {
+ final Set<MacAddress> connectedClientMacs;
+ switch (server.servingMode()) {
+ case IpServer.STATE_TETHERED:
+ connectedClientMacs = wifiClientMacs;
+ break;
+ case IpServer.STATE_LOCAL_ONLY:
+ // Before T, SAP and LOHS both use wifiClientMacs because
+ // registerLocalOnlyHotspotSoftApCallback didn't exist.
+ connectedClientMacs = SdkLevel.isAtLeastT()
+ ? localOnlyClientMacs : wifiClientMacs;
+ break;
+ default:
+ continue;
+ }
+
for (TetheredClient client : server.getAllLeases()) {
if (client.getTetheringType() == TETHERING_WIFI
- && !wifiClientMacs.contains(client.getMacAddress())) {
+ && !connectedClientMacs.contains(client.getMacAddress())) {
// Skip leases of WiFi clients that are not (or no longer) L2-connected
continue;
}
@@ -104,11 +127,8 @@
// TODO: add IPv6 addresses from netlink
// Add connected WiFi clients that do not have any known address
- for (MacAddress client : wifiClientMacs) {
- if (clientsMap.containsKey(client)) continue;
- clientsMap.put(client, new TetheredClient(
- client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
- }
+ addWifiClientsIfNoLeases(clientsMap, wifiClientMacs);
+ addWifiClientsIfNoLeases(clientsMap, localOnlyClientMacs);
final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
@@ -117,6 +137,15 @@
return clientsChanged;
}
+ private static void addWifiClientsIfNoLeases(
+ final Map<MacAddress, TetheredClient> clientsMap, final Set<MacAddress> clientMacs) {
+ for (MacAddress mac : clientMacs) {
+ if (clientsMap.containsKey(mac)) continue;
+ clientsMap.put(mac, new TetheredClient(
+ mac, Collections.emptyList() /* addresses */, TETHERING_WIFI));
+ }
+ }
+
private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
final TetheredClient aggregateClient = clientsMap.getOrDefault(
lease.getMacAddress(), lease);
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 6d502ce..b88b13b 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -459,8 +459,9 @@
}
@VisibleForTesting
- PendingIntent createRecheckAlarmIntent() {
+ PendingIntent createRecheckAlarmIntent(final String pkgName) {
final Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
+ intent.setPackage(pkgName);
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
@@ -470,7 +471,7 @@
final int period = config.provisioningCheckPeriod;
if (period <= 0) return;
- mProvisioningRecheckAlarm = createRecheckAlarmIntent();
+ mProvisioningRecheckAlarm = createRecheckAlarmIntent(mContext.getPackageName());
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
long triggerAtMillis = SystemClock.elapsedRealtime() + (period * MS_PER_HOUR);
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 41a10ae..6c0ca82 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -16,6 +16,8 @@
package com.android.networkstack.tethering;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -34,7 +36,6 @@
import android.net.ip.IpServer;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -77,7 +78,7 @@
private final ConnectivityManager mConnectivityMgr;
private final TetheringConfiguration mConfig;
// keyed by downstream type(TetheringManager.TETHERING_*).
- private final SparseArray<LinkAddress> mCachedAddresses;
+ private final ArrayMap<AddressKey, LinkAddress> mCachedAddresses;
public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
mDownstreams = new ArraySet<>();
@@ -85,10 +86,12 @@
mConnectivityMgr = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
mConfig = config;
- mCachedAddresses = new SparseArray<>();
+ mCachedAddresses = new ArrayMap<AddressKey, LinkAddress>();
// Reserved static addresses for bluetooth and wifi p2p.
- mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
- mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
+ mCachedAddresses.put(new AddressKey(TETHERING_BLUETOOTH, CONNECTIVITY_SCOPE_GLOBAL),
+ new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
+ mCachedAddresses.put(new AddressKey(TETHERING_WIFI_P2P, CONNECTIVITY_SCOPE_LOCAL),
+ new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"),
new IpPrefix("172.16.0.0/12"), new IpPrefix("10.0.0.0/8")));
@@ -166,16 +169,18 @@
* returns null if there is no available address.
*/
@Nullable
- public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+ public LinkAddress requestDownstreamAddress(final IpServer ipServer, final int scope,
+ boolean useLastAddress) {
if (mConfig.shouldEnableWifiP2pDedicatedIp()
&& ipServer.interfaceType() == TETHERING_WIFI_P2P) {
return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
}
+ final AddressKey addrKey = new AddressKey(ipServer.interfaceType(), scope);
// This ensures that tethering isn't started on 2 different interfaces with the same type.
// Once tethering could support multiple interface with the same type,
// TetheringSoftApCallback would need to handle it among others.
- final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
+ final LinkAddress cachedAddress = mCachedAddresses.get(addrKey);
if (useLastAddress && cachedAddress != null
&& !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
mDownstreams.add(ipServer);
@@ -186,7 +191,7 @@
final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
if (newAddress != null) {
mDownstreams.add(ipServer);
- mCachedAddresses.put(ipServer.interfaceType(), newAddress);
+ mCachedAddresses.put(addrKey, newAddress);
return newAddress;
}
}
@@ -384,6 +389,34 @@
return asIpPrefix(address);
}
+ private static class AddressKey {
+ private final int mTetheringType;
+ private final int mScope;
+
+ private AddressKey(int type, int scope) {
+ mTetheringType = type;
+ mScope = scope;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mTetheringType << 16) + mScope;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof AddressKey)) return false;
+ final AddressKey other = (AddressKey) obj;
+
+ return mTetheringType == other.mTetheringType && mScope == other.mScope;
+ }
+
+ @Override
+ public String toString() {
+ return "AddressKey(" + mTetheringType + ", " + mScope + ")";
+ }
+ }
+
void dump(final IndentingPrintWriter pw) {
pw.println("mTetheringPrefixes:");
pw.increaseIndent();
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 4c5bf4e..e5f644e 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -58,6 +58,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
+import static android.net.wifi.WifiManager.SoftApCallback;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -479,15 +480,15 @@
mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
final WifiManager wifiManager = getWifiManager();
- TetheringSoftApCallback softApCallback = new TetheringSoftApCallback();
if (wifiManager != null) {
- wifiManager.registerSoftApCallback(mExecutor, softApCallback);
- }
- if (SdkLevel.isAtLeastT() && wifiManager != null) {
- // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
- // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
- // or MAINLINE_NETWORK_STACK permission would also able to use this API.
- wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor, softApCallback);
+ wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+ if (SdkLevel.isAtLeastT()) {
+ // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
+ // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
+ // or MAINLINE_NETWORK_STACK permission can also use this API.
+ wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor,
+ new LocalOnlyHotspotCallback());
+ }
}
startTrackDefaultNetwork();
@@ -573,26 +574,17 @@
}
}
- private class TetheringSoftApCallback implements WifiManager.SoftApCallback {
- // TODO: Remove onStateChanged override when this method has default on
- // WifiManager#SoftApCallback interface.
- // Wifi listener for state change of the soft AP
- @Override
- public void onStateChanged(final int state, final int failureReason) {
- // Nothing
- }
-
- // Called by wifi when the number of soft AP clients changed.
- // Currently multiple softAp would not behave well in PrivateAddressCoordinator
- // (where it gets the address from cache), it ensure tethering only support one ipServer for
- // TETHERING_WIFI. Once tethering support multiple softAp enabled simultaneously,
- // onConnectedClientsChanged should also be updated to support tracking different softAp's
- // clients individually.
- // TODO: Add wtf log and have check to reject request duplicated type with different
- // interface.
+ private class TetheringSoftApCallback implements SoftApCallback {
@Override
public void onConnectedClientsChanged(final List<WifiClient> clients) {
- updateConnectedClients(clients);
+ updateConnectedClients(clients, null);
+ }
+ }
+
+ private class LocalOnlyHotspotCallback implements SoftApCallback {
+ @Override
+ public void onConnectedClientsChanged(final List<WifiClient> clients) {
+ updateConnectedClients(null, clients);
}
}
@@ -1968,7 +1960,7 @@
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
mForwardedDownstreams.remove(who);
- updateConnectedClients(null /* wifiClients */);
+ maybeDhcpLeasesChanged();
// If this is a Wi-Fi interface, tell WifiManager of any errors
// or the inactive serving state.
@@ -2710,9 +2702,15 @@
if (e != null) throw e;
}
- private void updateConnectedClients(final List<WifiClient> wifiClients) {
+ private void maybeDhcpLeasesChanged() {
+ // null means wifi clients did not change.
+ updateConnectedClients(null, null);
+ }
+
+ private void updateConnectedClients(final List<WifiClient> wifiClients,
+ final List<WifiClient> localOnlyClients) {
if (mConnectedClientsTracker.updateConnectedClients(mTetherMainSM.getAllDownstreams(),
- wifiClients)) {
+ wifiClients, localOnlyClients)) {
reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
}
}
@@ -2731,7 +2729,7 @@
@Override
public void dhcpLeasesChanged() {
- updateConnectedClients(null /* wifiClients */);
+ maybeDhcpLeasesChanged();
}
@Override
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f0d9057..46e50ef 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -19,6 +19,8 @@
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
@@ -48,6 +50,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -271,8 +274,8 @@
dispatchTetherConnectionChanged(upstreamIface, lp, 0);
}
reset(mNetd, mCallback, mAddressCoordinator, mBpfCoordinator);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(mTestAddress);
}
@SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run()
@@ -293,8 +296,8 @@
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(mTestAddress);
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
@@ -428,7 +431,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
if (isAtLeastT()) {
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
}
@@ -477,7 +481,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -498,7 +503,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -766,7 +772,8 @@
final ArgumentCaptor<LinkProperties> lpCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(true));
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
// One for ipv4 route, one for ipv6 link local route.
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
@@ -779,12 +786,13 @@
// Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
// onNewPrefixRequest callback.
final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- newAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(newAddress);
eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
mLooper.dispatchAll();
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(false));
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
verifyNoMoreInteractions(mCallback);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
index d915354..2dd9f91 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
@@ -24,19 +24,25 @@
import android.net.TetheringManager.TETHERING_WIFI
import android.net.ip.IpServer
import android.net.wifi.WifiClient
+import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
@SmallTest
class ConnectedClientsTrackerTest {
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule()
private val server1 = mock(IpServer::class.java)
private val server2 = mock(IpServer::class.java)
@@ -70,55 +76,122 @@
@Test
fun testUpdateConnectedClients() {
+ doReturn(IpServer.STATE_TETHERED).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
+ runUpdateConnectedClientsTest(isGlobal = true)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ fun testUpdateConnectedClients_LocalOnly() {
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server2).servingMode()
+ runUpdateConnectedClientsTest(isGlobal = false)
+ }
+
+ fun runUpdateConnectedClientsTest(isGlobal: Boolean) {
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
val tracker = ConnectedClientsTracker(clock)
- assertFalse(tracker.updateConnectedClients(servers, null))
+ assertFalse(tracker.updateConnectedClients(servers, null, null))
// Obtain a lease for client 1
doReturn(listOf(client1)).`when`(server1).allLeases
- assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1)))
+ if (isGlobal) {
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ wifiClients = listOf(wifiClient1)))
+ } else {
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ localOnlyClients = listOf(wifiClient1)))
+ }
// Client 2 L2-connected, no lease yet
val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI)
- assertSameClients(listOf(client1, client2WithoutAddr),
- assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2)))
+ if (isGlobal) {
+ assertSameClients(listOf(client1, client2WithoutAddr), assertNewClients(
+ tracker, servers, wifiClients = listOf(wifiClient1, wifiClient2)))
+ } else {
+ assertSameClients(listOf(client1, client2WithoutAddr), assertNewClients(
+ tracker, servers, localOnlyClients = listOf(wifiClient1, wifiClient2)))
+ }
// Client 2 lease obtained
doReturn(listOf(client1, client2)).`when`(server1).allLeases
- assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null))
+ assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers))
// Client 3 lease obtained
doReturn(listOf(client3)).`when`(server2).allLeases
- assertSameClients(listOf(client1, client2, client3),
- assertNewClients(tracker, servers, null))
+ assertSameClients(listOf(client1, client2, client3), assertNewClients(tracker, servers))
- // Client 2 L2-disconnected
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
-
- // Client 1 L2-disconnected
- assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList()))
-
- // Client 1 comes back
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
+ if (isGlobal) {
+ // Client 2 L2-disconnected
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, wifiClients = listOf(wifiClient1)))
+ // Client 1 L2-disconnected
+ assertSameClients(listOf(client3), assertNewClients(tracker, servers,
+ wifiClients = emptyList()))
+ // Client 1 comes back
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, wifiClients = listOf(wifiClient1)))
+ } else {
+ // Client 2 L2-disconnected
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, localOnlyClients = listOf(wifiClient1)))
+ // Client 1 L2-disconnected
+ assertSameClients(listOf(client3),
+ assertNewClients(tracker, servers, localOnlyClients = emptyList()))
+ // Client 1 comes back
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, localOnlyClients = listOf(wifiClient1)))
+ }
// Leases lost, client 1 still L2-connected
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)),
- assertNewClients(tracker, servers, null))
+ assertNewClients(tracker, servers))
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ fun testLocalOnlyAndTetheredHotspotClients() {
+ val tracker = ConnectedClientsTracker(clock)
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
+
+ // Client 1 connected to server1 (LOHS)
+ doReturn(listOf(client1)).`when`(server1).allLeases
+ doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ localOnlyClients = listOf(wifiClient1)))
+
+ // Client 2 connected to server2 (wifi Tethering)
+ doReturn(listOf(client2)).`when`(server2).allLeases
+ assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers,
+ listOf(wifiClient2), listOf(wifiClient1)))
+
+ // Client 2 L2-disconnected but lease doesn't expired yet
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ wifiClients = emptyList()))
+
+ // Client 1 lease lost but still L2-connected
+ doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+ val client1WithoutAddr = TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)
+ assertSameClients(listOf(client1WithoutAddr), assertNewClients(tracker, servers))
+
+ // Client 1 L2-disconnected
+ assertSameClients(emptyList(), assertNewClients(tracker, servers,
+ localOnlyClients = emptyList()))
}
@Test
fun testUpdateConnectedClients_LeaseExpiration() {
+ doReturn(IpServer.STATE_TETHERED).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
val tracker = ConnectedClientsTracker(clock)
doReturn(listOf(client1, client2)).`when`(server1).allLeases
doReturn(listOf(client3)).`when`(server2).allLeases
assertSameClients(listOf(client1, client2, client3), assertNewClients(
- tracker, servers, listOf(wifiClient1, wifiClient2)))
+ tracker, servers, wifiClients = listOf(wifiClient1, wifiClient2)))
clock.time += 20
// Client 3 has no remaining lease: removed
@@ -131,15 +204,16 @@
// Only the "t + 30" address is left, the "t + 10" address expired
listOf(client2Exp30AddrInfo),
TETHERING_WIFI))
- assertSameClients(expectedClients, assertNewClients(tracker, servers, null))
+ assertSameClients(expectedClients, assertNewClients(tracker, servers))
}
private fun assertNewClients(
tracker: ConnectedClientsTracker,
ipServers: Iterable<IpServer>,
- wifiClients: List<WifiClient>?
+ wifiClients: List<WifiClient>? = null,
+ localOnlyClients: List<WifiClient>? = null
): List<TetheredClient> {
- assertTrue(tracker.updateConnectedClients(ipServers, wifiClients))
+ assertTrue(tracker.updateConnectedClients(ipServers, wifiClients, localOnlyClients))
return tracker.lastTetheredClients
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index e4263db..c2e1617 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -213,7 +213,8 @@
}
@Override
- PendingIntent createRecheckAlarmIntent() {
+ PendingIntent createRecheckAlarmIntent(final String pkgName) {
+ assertEquals(TEST_PACKAGE_NAME, pkgName);
return mAlarmIntent;
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 55d9852..91b092a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -19,6 +19,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -61,6 +63,7 @@
private static final String TEST_IFNAME = "test0";
@Mock private IpServer mHotspotIpServer;
+ @Mock private IpServer mLocalHotspotIpServer;
@Mock private IpServer mUsbIpServer;
@Mock private IpServer mEthernetIpServer;
@Mock private IpServer mWifiP2pIpServer;
@@ -90,6 +93,7 @@
when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
+ when(mLocalHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
}
@@ -104,9 +108,10 @@
mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
}
- private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+ private LinkAddress requestDownstreamAddress(final IpServer ipServer, int scope,
+ boolean useLastAddress) {
final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- ipServer, useLastAddress);
+ ipServer, scope, useLastAddress);
when(ipServer.getAddress()).thenReturn(address);
return address;
}
@@ -115,19 +120,19 @@
public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception {
final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress);
final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
assertNotEquals(hotspotPrefix, bluetoothPrefix);
final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix testDupRequest = asIpPrefix(newAddress);
assertNotEquals(hotspotPrefix, testDupRequest);
assertNotEquals(bluetoothPrefix, testDupRequest);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(usbPrefix, bluetoothPrefix);
assertNotEquals(usbPrefix, hotspotPrefix);
@@ -139,25 +144,28 @@
int fakeSubAddr = 0x2b00; // 43.0.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2d01; // 45.1.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2eff; // 46.255.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2f05; // 47.5.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
}
@@ -168,7 +176,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mBluetoothAddress.getAddress().getAddress()));
final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
@@ -177,7 +185,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(hotspotAddress.getAddress().getAddress()));
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix);
assertNotEquals(hotspotPrefix, usbPrefix);
@@ -187,7 +195,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix etherPrefix = asIpPrefix(etherAddress);
assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix);
assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix);
@@ -200,11 +208,11 @@
final int fakeHotspotSubAddr = 0x2b05; // 43.5
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
@@ -214,10 +222,10 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals(hotspotAddress, newHotspotAddress);
final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals(usbAddress, newUsbAddress);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -257,7 +265,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
// - Enable hotspot with prefix 192.168.43.0/24
final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr);
assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
// - test mobile network with null NetworkCapabilities. Ideally this should not happen
@@ -311,21 +319,21 @@
// - Restart hotspot again and its prefix is different previous.
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2);
assertNotEquals(hotspotPrefix, hotspotPrefix2);
mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
// - Usb tethering can be enabled and its prefix is different with conflict one.
final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddr);
assertNotEquals(predefinedPrefix, usbPrefix);
assertNotEquals(hotspotPrefix2, usbPrefix);
// - Disable wifi upstream, then wifi's prefix can be selected again.
mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix ethPrefix = asIpPrefix(ethAddr);
assertEquals(predefinedPrefix, ethPrefix);
}
@@ -335,7 +343,7 @@
final int randomAddress = 0x8605; // 134.5
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
// Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -345,7 +353,7 @@
// Check whether return address is next prefix of 192.168.134.0/24.
final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
new LinkAddress("192.168.149.16/19"), null,
@@ -355,7 +363,7 @@
// The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
new LinkAddress("192.168.129.53/18"), null,
@@ -370,7 +378,7 @@
// The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
new LinkAddress("192.168.188.133/17"), null,
@@ -380,7 +388,7 @@
// Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
// 192.168.134/24 ~ 192.168.255.255/24 is not available.
final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
new LinkAddress("192.168.3.59/21"), null,
@@ -389,7 +397,7 @@
// Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
new LinkAddress("192.168.68.43/21"), null,
@@ -399,7 +407,7 @@
// Update an upstream that does *not* conflict, check whether return the same address
// 192.168.5/24.
final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
new LinkAddress("192.168.10.97/21"), null,
@@ -408,7 +416,7 @@
// Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
new LinkAddress("192.168.0.0/17"), null,
@@ -417,7 +425,7 @@
// Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
}
@@ -426,7 +434,7 @@
final int randomAddress = 0x1f2b2a; // 31.43.42
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
// Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -437,7 +445,7 @@
// Check whether return address is next address of prefix 192.168.128.0/17.
final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
new LinkAddress("192.1.2.3/8"), null,
@@ -447,7 +455,7 @@
// Check whether return address is under prefix 172.16.0.0/12.
final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
new LinkAddress("172.28.123.100/14"), null,
@@ -458,12 +466,12 @@
// 172.28.0.0 ~ 172.31.255.255 is not available.
// Check whether return address is next address of prefix 172.16.0.0/14.
final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
// Check whether new downstream is next address of address 172.16.0.42/24.
final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
new LinkAddress("172.16.0.1/24"), null,
@@ -474,7 +482,7 @@
// Check whether return address is next address of prefix 172.16.1.42/24.
final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
new LinkAddress("172.16.0.1/13"), null,
@@ -485,11 +493,11 @@
// Check whether return address is next address of prefix 172.16.0.1/13.
final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
// Check whether return address is next address of prefix 172.24.0.42/24.
final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
new LinkAddress("172.24.0.1/12"), null,
@@ -500,11 +508,11 @@
// Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
// Check whether new downstream is next address of address 10.31.43.42/24.
final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
}
@@ -524,7 +532,7 @@
private void assertReseveredWifiP2pPrefix() throws Exception {
LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress);
assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
@@ -544,8 +552,23 @@
// If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_LOCAL, true /* useLastAddress */);
assertEquals(mLegacyWifiP2pAddress, address);
mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
}
+
+ @Test
+ public void testEnableSapAndLohsConcurrently() throws Exception {
+ // 0x2b05 -> 43.5, 0x8605 -> 134.5
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(0x2b05, 0x8605);
+
+ final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
+ assertEquals("Wrong hotspot prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
+
+ final LinkAddress localHotspotAddress = requestDownstreamAddress(mLocalHotspotIpServer,
+ CONNECTIVITY_SCOPE_LOCAL, true /* useLastAddress */);
+ assertEquals("Wrong local hotspot prefix: ", new LinkAddress("192.168.134.5/24"),
+ localHotspotAddress);
+ }
}
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 c15b85e..bd8b325 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2988,9 +2988,9 @@
final MacAddress testMac1 = MacAddress.fromString("11:11:11:11:11:11");
final DhcpLeaseParcelable p2pLease = createDhcpLeaseParcelable("clientId1", testMac1,
"192.168.50.24", 24, Long.MAX_VALUE, "test1");
- final List<TetheredClient> p2pClients = notifyDhcpLeasesChanged(TETHERING_WIFI_P2P,
+ final List<TetheredClient> connectedClients = notifyDhcpLeasesChanged(TETHERING_WIFI_P2P,
eventCallbacks, p2pLease);
- callback.expectTetheredClientChanged(p2pClients);
+ callback.expectTetheredClientChanged(connectedClients);
reset(mDhcpServer);
// Run wifi tethering.
@@ -2999,21 +2999,11 @@
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), dhcpEventCbsCaptor.capture());
eventCallbacks = dhcpEventCbsCaptor.getValue();
- // Update mac address from softAp callback before getting dhcp lease.
final MacAddress testMac2 = MacAddress.fromString("22:22:22:22:22:22");
- final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac2,
- false /* isLocalOnly */);
- final List<TetheredClient> p2pAndNoAddrClients = new ArrayList<>(p2pClients);
- p2pAndNoAddrClients.add(noAddrClient);
- callback.expectTetheredClientChanged(p2pAndNoAddrClients);
-
- // Update dhcp lease for wifi tethering.
final DhcpLeaseParcelable wifiLease = createDhcpLeaseParcelable("clientId2", testMac2,
"192.168.43.24", 24, Long.MAX_VALUE, "test2");
- final List<TetheredClient> p2pAndWifiClients = new ArrayList<>(p2pClients);
- p2pAndWifiClients.addAll(notifyDhcpLeasesChanged(TETHERING_WIFI,
- eventCallbacks, wifiLease));
- callback.expectTetheredClientChanged(p2pAndWifiClients);
+ verifyHotspotClientUpdate(false /* isLocalOnly */, testMac2, wifiLease, connectedClients,
+ eventCallbacks, callback);
// Test onStarted callback that register second callback when tethering is running.
TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
@@ -3021,7 +3011,7 @@
mTethering.registerTetheringEventCallback(callback2);
mLooper.dispatchAll();
});
- callback2.expectTetheredClientChanged(p2pAndWifiClients);
+ callback2.expectTetheredClientChanged(connectedClients);
}
@Test
@@ -3043,26 +3033,34 @@
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), dhcpEventCbsCaptor.capture());
final IDhcpEventCallbacks eventCallbacks = dhcpEventCbsCaptor.getValue();
- // Update mac address from softAp callback before getting dhcp lease.
- final MacAddress testMac = MacAddress.fromString("22:22:22:22:22:22");
- final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac,
- true /* isLocalOnly */);
- final List<TetheredClient> noAddrLocalOnlyClients = new ArrayList<>();
- noAddrLocalOnlyClients.add(noAddrClient);
- callback.expectTetheredClientChanged(noAddrLocalOnlyClients);
- // Update dhcp lease for local only hotspot.
+ final List<TetheredClient> connectedClients = new ArrayList<>();
+ final MacAddress testMac = MacAddress.fromString("22:22:22:22:22:22");
final DhcpLeaseParcelable wifiLease = createDhcpLeaseParcelable("clientId", testMac,
"192.168.43.24", 24, Long.MAX_VALUE, "test");
- final List<TetheredClient> localOnlyClients = notifyDhcpLeasesChanged(TETHERING_WIFI,
- eventCallbacks, wifiLease);
- callback.expectTetheredClientChanged(localOnlyClients);
+ verifyHotspotClientUpdate(true /* isLocalOnly */, testMac, wifiLease, connectedClients,
+ eventCallbacks, callback);
// Client disconnect from local only hotspot.
mLocalOnlyHotspotCallback.onConnectedClientsChanged(Collections.emptyList());
callback.expectTetheredClientChanged(Collections.emptyList());
}
+ private void verifyHotspotClientUpdate(final boolean isLocalOnly, final MacAddress testMac,
+ final DhcpLeaseParcelable dhcpLease, final List<TetheredClient> currentClients,
+ final IDhcpEventCallbacks dhcpCallback, final TestTetheringEventCallback callback)
+ throws Exception {
+ // Update mac address from softAp callback before getting dhcp lease.
+ final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac, isLocalOnly);
+ final List<TetheredClient> withNoAddrClients = new ArrayList<>(currentClients);
+ withNoAddrClients.add(noAddrClient);
+ callback.expectTetheredClientChanged(withNoAddrClients);
+
+ // Update dhcp lease for hotspot.
+ currentClients.addAll(notifyDhcpLeasesChanged(TETHERING_WIFI, dhcpCallback, dhcpLease));
+ callback.expectTetheredClientChanged(currentClients);
+ }
+
private TetheredClient notifyConnectedWifiClientsChanged(final MacAddress mac,
boolean isLocalOnly) throws Exception {
final ArrayList<WifiClient> wifiClients = new ArrayList<>();
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 839ca40..245ad7a 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -412,10 +412,8 @@
// Always allow and never count clat traffic. Only the IPv4 traffic on the stacked
// interface is accounted for and subject to usage restrictions.
- // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
- if (sock_uid == AID_CLAT || uid == AID_CLAT) {
- return PASS;
- }
+ // CLAT IPv6 TX sockets are *always* tagged with CLAT uid, see tagSocketAsClat()
+ if (uid == AID_CLAT) return PASS;
int match = bpf_owner_match(skb, sock_uid, egress, kver);
@@ -441,17 +439,17 @@
uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
uint32_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
- // Use asm("%0 &= 1" : "+r"(match)) before return match,
- // to help kernel's bpf verifier, so that it can be 100% certain
- // that the returned value is always BPF_NOMATCH(0) or BPF_MATCH(1).
- if (!selectedMap) {
- asm("%0 &= 1" : "+r"(match));
- return match;
- }
+ if (!selectedMap) return PASS; // cannot happen, needed to keep bpf verifier happy
do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
update_stats_with_config(*selectedMap, skb, &key, egress, kver);
update_app_uid_stats_map(skb, &uid, egress, kver);
+
+ // We've already handled DROP_UNLESS_DNS up above, thus when we reach here the only
+ // possible values of match are DROP(0) or PASS(1), however we need to use
+ // "match &= 1" before 'return match' to help the kernel's bpf verifier,
+ // so that it can be 100% certain that the returned value is always 0 or 1.
+ // We use assembly so that it cannot be optimized out by a too smart compiler.
asm("%0 &= 1" : "+r"(match));
return match;
}
@@ -502,9 +500,8 @@
// Clat daemon does not generate new traffic, all its traffic is accounted for already
// on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
// but that can be corrected for later when merging v4-foo stats into interface foo's).
- // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
+ // CLAT sockets are created by system server and tagged as uid CLAT, see tagSocketAsClat()
uint32_t sock_uid = bpf_get_socket_uid(skb);
- if (sock_uid == AID_CLAT) return BPF_NOMATCH;
if (sock_uid == AID_SYSTEM) {
uint64_t cookie = bpf_get_socket_cookie(skb);
UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 822e67d..67dacb8 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -28,6 +28,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager.MultipathPreference;
import android.os.Binder;
import android.os.Build;
@@ -36,6 +37,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Range;
import com.android.net.module.util.ConnectivitySettingsUtils;
@@ -55,6 +57,7 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public class ConnectivitySettingsManager {
+ private static final String TAG = ConnectivitySettingsManager.class.getSimpleName();
private ConnectivitySettingsManager() {}
@@ -696,10 +699,20 @@
/**
* Set global http proxy settings from given {@link ProxyInfo}.
*
+ * <p class="note">
+ * While a {@link ProxyInfo} for a PAC proxy can be specified, not all devices support
+ * PAC proxies. In particular, smaller devices like watches often do not have the capabilities
+ * necessary to interpret the PAC file. In such cases, calling this API with a PAC proxy
+ * results in undefined behavior, including possibly breaking networking for applications.
+ * You can test for this by checking for the presence of {@link PackageManager.FEATURE_WEBVIEW}.
+ * </p>
+ *
* @param context The {@link Context} to set the setting.
* @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
* {@link ProxyInfo#buildPacProxy(Uri)} or
* {@link ProxyInfo#buildDirectProxy(String, int, List)}
+ * @throws UnsupportedOperationException if |proxyInfo| codes for a PAC proxy but the system
+ * does not support PAC proxies.
*/
public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
final String host = proxyInfo.getHost();
@@ -707,6 +720,14 @@
final String exclusionList = proxyInfo.getExclusionListAsString();
final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
+
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ final PackageManager pm = context.getPackageManager();
+ if (null != pm && !pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ Log.wtf(TAG, "PAC proxy can't be installed on a device without FEATURE_WEBVIEW");
+ }
+ }
+
if (TextUtils.isEmpty(pacFileUrl)) {
Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index 4f7ac30..9fb9bc6 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -1456,8 +1456,13 @@
* @hide
*/
public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
- // list order is important, compare one by one
- return target.getPcscfServers().equals(mPcscfs);
+ // Per 3GPP TS 24.229, B.2.2.1 PDP context activation and P-CSCF discovery
+ // list order is important, so on U+ compare one by one
+ if (SdkLevel.isAtLeastU()) return target.getPcscfServers().equals(mPcscfs);
+ // but for safety old behaviour on pre-U:
+ Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+ return (mPcscfs.size() == targetPcscfs.size()) ?
+ mPcscfs.containsAll(targetPcscfs) : false;
}
/**
diff --git a/framework/src/android/net/SocketKeepalive.java b/framework/src/android/net/SocketKeepalive.java
index 10daf17..f915e72 100644
--- a/framework/src/android/net/SocketKeepalive.java
+++ b/framework/src/android/net/SocketKeepalive.java
@@ -149,9 +149,7 @@
public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
/**
- * There was no such slot. This should only be internally as it indicates
- * a programming error in the system server. It should not propagate to
- * applications.
+ * There was no such slot, or no keepalive running on this slot.
* @hide
*/
@SystemApi
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index 40191cf..451909c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -61,13 +61,11 @@
final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
msg.getFlags(), ifaddrMsg.scope);
- if (!la.isPreferred()) {
- // Skip the unusable ip address.
- return;
- }
switch (msg.getHeader().nlmsg_type) {
case NetlinkConstants.RTM_NEWADDR:
- mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+ if (la.isPreferred()) {
+ mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+ }
break;
case NetlinkConstants.RTM_DELADDR:
mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp
index 3e4c4de..08d31a3 100644
--- a/service/jni/com_android_server_TestNetworkService.cpp
+++ b/service/jni/com_android_server_TestNetworkService.cpp
@@ -38,7 +38,7 @@
#include "jni.h"
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-#include <bpf/KernelVersion.h>
+#include <bpf/KernelUtils.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index d966070..c125bd6 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -475,7 +475,7 @@
static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
jint pid) {
if (pid <= 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid pid");
return;
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index c667d72..51df1dd 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -109,7 +109,6 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.UidFrozenStateChangedCallback;
@@ -1519,6 +1518,23 @@
throws SocketException, InterruptedIOException, ErrnoException {
InetDiagMessage.destroyLiveTcpSocketsByOwnerUids(ownerUids);
}
+
+ /**
+ * Schedule the evaluation timeout.
+ *
+ * When a network connects, it's "not evaluated" yet. Detection events cause the network
+ * to be "evaluated" (typically, validation or detection of a captive portal). If none
+ * of these events happen, this time will run out, after which the network is considered
+ * "evaluated" even if nothing happened to it. Notionally that means the system gave up
+ * on this network and considers it won't provide connectivity. In particular, that means
+ * it's when the system prefers it to cell if it's wifi and configuration says it should
+ * prefer bad wifi to cell.
+ */
+ public void scheduleEvaluationTimeout(@NonNull Handler handler,
+ @NonNull final Network network, final long delayMs) {
+ handler.sendMessageDelayed(
+ handler.obtainMessage(EVENT_INITIAL_EVALUATION_TIMEOUT, network), delayMs);
+ }
}
public ConnectivityService(Context context) {
@@ -3178,8 +3194,6 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
// TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
@TargetApi(Build.VERSION_CODES.S)
private void sendStickyBroadcast(Intent intent) {
@@ -5273,8 +5287,7 @@
/** Schedule evaluation timeout */
@VisibleForTesting
public void scheduleEvaluationTimeout(@NonNull final Network network, final long delayMs) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(EVENT_INITIAL_EVALUATION_TIMEOUT, network), delayMs);
+ mDeps.scheduleEvaluationTimeout(mHandler, network, delayMs);
}
@Override
@@ -8791,8 +8804,6 @@
// else not handled
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
private void sendIntent(PendingIntent pendingIntent, Intent intent) {
mPendingIntentWakeLock.acquire();
try {
@@ -9826,7 +9837,7 @@
networkAgent.networkMonitor().notifyNetworkConnected(params.linkProperties,
params.networkCapabilities);
}
- final long delay = activelyPreferBadWifi()
+ final long delay = !avoidBadWifi() && activelyPreferBadWifi()
? ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS
: DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS;
scheduleEvaluationTimeout(networkAgent.network, delay);
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index e2ef981..f8285ed 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -210,8 +210,12 @@
this.mKi = Objects.requireNonNull(ki);
mCallback = ki.mCallback;
mUnderpinnedNetwork = underpinnedNetwork;
- if (autoOnOff && mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
- true /* defaultEnabled */)) {
+ // Reading DeviceConfig will check if the calling uid and calling package name are the
+ // same. Clear calling identity to align the calling uid and package
+ final boolean enabled = BinderUtils.withCleanCallingIdentity(
+ () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
+ true /* defaultEnabled */));
+ if (autoOnOff && enabled) {
mAutomaticOnOffState = STATE_ENABLED;
if (null == ki.mFd) {
throw new IllegalArgumentException("fd can't be null with automatic "
@@ -583,8 +587,12 @@
*/
public void dump(IndentingPrintWriter pw) {
mKeepaliveTracker.dump(pw);
- final boolean featureEnabled = mDependencies.isFeatureEnabled(
- AUTOMATIC_ON_OFF_KEEPALIVE_VERSION, true /* defaultEnabled */);
+ // Reading DeviceConfig will check if the calling uid and calling package name are the same.
+ // Clear calling identity to align the calling uid and package so that it won't fail if cts
+ // would like to call dump()
+ final boolean featureEnabled = BinderUtils.withCleanCallingIdentity(
+ () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
+ true /* defaultEnabled */));
pw.println("AutomaticOnOff enabled: " + featureEnabled);
pw.increaseIndent();
for (AutomaticOnOffKeepalive autoKi : mAutomaticOnOffKeepalives) {
@@ -837,12 +845,8 @@
* @return whether the feature is enabled
*/
public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
- // Reading DeviceConfig will check if the calling uid and calling package name are the
- // same. Clear calling identity to align the calling uid and package so that it won't
- // fail if cts would like to do the dump()
- return BinderUtils.withCleanCallingIdentity(() ->
- DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
- DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled));
+ return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
+ DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled);
}
/**
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 8c170bc..cc226ce 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -569,7 +569,20 @@
} else if (reason == ERROR_STOP_REASON_UNINITIALIZED) {
throw new IllegalStateException("Unexpected stop reason: " + reason);
} else if (reason == ERROR_NO_SUCH_SLOT) {
- throw new IllegalStateException("No such slot: " + reason);
+ // There are multiple independent reasons a keepalive can stop. Some
+ // are software (e.g. the app stops the keepalive) and some are hardware
+ // (e.g. the SIM card gets removed). Therefore, there is a very low
+ // probability that both of these happen at the same time, which would
+ // result in the first stop attempt returning SUCCESS and the second
+ // stop attempt returning NO_SUCH_SLOT. Such a race condition can be
+ // ignored with a log.
+ // This should still be reported because if it happens with any frequency
+ // it probably means there is a bug where the system server is trying
+ // to use a non-existing hardware slot.
+ // TODO : separate the non-existing hardware slot from the case where
+ // there is no keepalive running on this slot.
+ Log.wtf(TAG, "Keepalive on slot " + slot + " can't be stopped : " + reason);
+ notifyErrorCallback(ki.mCallback, reason);
} else {
notifyErrorCallback(ki.mCallback, reason);
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index af8938a..cf5fc50 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -172,6 +172,7 @@
// for modules other than Connectivity does not provide much value. Only run them in connectivity
// module MTS, so the tests only need to cover the case of an updated NetworkAgent.
@ConnectivityModuleTest
+@AppModeFull(reason = "Instant apps can't use NetworkAgent because it needs NETWORK_FACTORY'.")
class NetworkAgentTest {
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2")
@@ -982,13 +983,11 @@
.also { assertNotNull(agent.network?.bindSocket(it)) }
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackRegisterAndUnregister() {
validateQosCallbackRegisterAndUnregister(IPPROTO_TCP)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackRegisterAndUnregisterWithDatagramSocket() {
validateQosCallbackRegisterAndUnregister(IPPROTO_UDP)
@@ -1025,13 +1024,11 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnQosSession() {
validateQosCallbackOnQosSession(IPPROTO_TCP)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnQosSessionWithDatagramSocket() {
validateQosCallbackOnQosSession(IPPROTO_UDP)
@@ -1090,7 +1087,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnError() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1129,7 +1125,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackIdsAreMappedCorrectly() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1170,7 +1165,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackWhenNetworkReleased() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1212,7 +1206,6 @@
)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testUnregisterAfterReplacement() {
// Keeps an eye on all test networks.
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index d8a0b07..83b9b81 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -799,7 +799,7 @@
// harness, which is untagged, won't cause a failure.
long firstTotal = resultsWithTraffic.get(0).total;
for (QueryResult queryResult : resultsWithTraffic) {
- assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 10);
+ assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 12);
}
// Expect to see no traffic when querying for any tag in tagsWithNoTraffic or any
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 88b9baf..9808137 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -73,8 +73,8 @@
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
import android.util.Log
+import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.PollingCheck
import com.android.compatibility.common.util.PropertyUtil
import com.android.modules.utils.build.SdkLevel.isAtLeastU
@@ -84,6 +84,8 @@
import com.android.networkstack.apishim.common.NsdShim
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.TestableNetworkAgent
@@ -129,12 +131,15 @@
// tried sequentially
private const val REGISTRATION_TIMEOUT_MS = 10_000L
private const val DBG = false
+private const val TEST_PORT = 12345
private val nsdShim = NsdShimImpl.newInstance()
@AppModeFull(reason = "Socket cannot bind in instant app mode")
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
@ConnectivityModuleTest
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
class NsdManagerTest {
// Rule used to filter CtsNetTestCasesMaxTargetSdkXX
@get:Rule
@@ -436,6 +441,13 @@
return agent
}
+ private fun makeTestServiceInfo(network: Network? = null) = NsdServiceInfo().also {
+ it.serviceType = serviceType
+ it.serviceName = serviceName
+ it.network = network
+ it.port = TEST_PORT
+ }
+
@After
fun tearDown() {
if (TestUtils.shouldTestTApis()) {
@@ -1048,6 +1060,52 @@
assertEquals(NsdManager.FAILURE_OPERATION_NOT_RUNNING, failedCb.errorCode)
}
+ @Test
+ fun testSubtypeAdvertisingAndDiscovery() {
+ val si = makeTestServiceInfo(network = testNetwork1.network)
+ // Test "_type._tcp.local,_subtype" syntax with the registration
+ si.serviceType = si.serviceType + ",_subtype"
+
+ val registrationRecord = NsdRegistrationRecord()
+
+ val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
+ val subtypeDiscoveryRecord = NsdDiscoveryRecord()
+ val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
+ tryTest {
+ registerService(registrationRecord, si)
+
+ // Test "_subtype._type._tcp.local" syntax with discovery. Note this is not
+ // "_subtype._sub._type._tcp.local".
+ nsdManager.discoverServices(serviceType,
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
+ nsdManager.discoverServices("_othersubtype.$serviceType",
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
+ nsdManager.discoverServices("_subtype.$serviceType",
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, subtypeDiscoveryRecord)
+
+ subtypeDiscoveryRecord.waitForServiceDiscovered(
+ serviceName, serviceType, testNetwork1.network)
+ baseTypeDiscoveryRecord.waitForServiceDiscovered(
+ serviceName, serviceType, testNetwork1.network)
+ otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
+ // The subtype callback was registered later but called, no need for an extra delay
+ otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
+ nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord)
+ nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
+
+ baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ subtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
/**
* Register a service and return its registration record.
*/
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index e6ffecd..e434649 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -387,6 +387,7 @@
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
+import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.DestroySocketsWrapper;
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
import com.android.server.connectivity.ApplicationSelfCertifiedNetworkCapabilities;
import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker;
@@ -615,6 +616,7 @@
@Mock TetheringManager mTetheringManager;
@Mock BroadcastOptionsShim mBroadcastOptionsShim;
@Mock ActivityManager mActivityManager;
+ @Mock DestroySocketsWrapper mDestroySocketsWrapper;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -1865,7 +1867,7 @@
final Context mockResContext = mock(Context.class);
doReturn(mResources).when(mockResContext).getResources();
ConnectivityResources.setResourcesContextForTest(mockResContext);
- mDeps = spy(new ConnectivityServiceDependencies(mockResContext));
+ mDeps = new ConnectivityServiceDependencies(mockResContext);
mAutoOnOffKeepaliveDependencies =
new AutomaticOnOffKeepaliveTrackerDependencies(mServiceContext);
mService = new ConnectivityService(mServiceContext,
@@ -1928,8 +1930,7 @@
R.integer.config_networkWakeupPacketMark);
}
- // ConnectivityServiceDependencies is public to use Mockito.spy
- public class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
+ class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
final ConnectivityResources mConnRes;
ConnectivityServiceDependencies(final Context mockResContext) {
@@ -2168,15 +2169,33 @@
}
}
- @Override
- public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
- final Set<Integer> exemptUids) {
- // This function is empty since the invocation of this method is verified by mocks
+ // Class to be mocked and used to verify destroy sockets methods call
+ public class DestroySocketsWrapper {
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids){}
+ public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids){}
}
- @Override
+ @Override @SuppressWarnings("DirectInvocationOnMock")
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids) {
+ // Call mocked destroyLiveTcpSockets so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSockets(ranges, exemptUids);
+ }
+
+ @Override @SuppressWarnings("DirectInvocationOnMock")
public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids) {
- // This function is empty since the invocation of this method is verified by mocks
+ // Call mocked destroyLiveTcpSocketsByOwnerUids so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSocketsByOwnerUids(ownerUids);
+ }
+
+ final ArrayTrackRecord<Long>.ReadHead mScheduledEvaluationTimeouts =
+ new ArrayTrackRecord<Long>().newReadHead();
+ @Override
+ public void scheduleEvaluationTimeout(@NonNull Handler handler,
+ @NonNull final Network network, final long delayMs) {
+ mScheduledEvaluationTimeouts.add(delayMs);
+ super.scheduleEvaluationTimeout(handler, network, delayMs);
}
}
@@ -6042,10 +6061,13 @@
wifiCallback.assertNoCallback();
}
- public void doTestPreferBadWifi(final boolean preferBadWifi) throws Exception {
+ public void doTestPreferBadWifi(final boolean avoidBadWifi,
+ final boolean preferBadWifi,
+ @NonNull Predicate<Long> checkUnvalidationTimeout) throws Exception {
// Pretend we're on a carrier that restricts switching away from bad wifi, and
// depending on the parameter one that may indeed prefer bad wifi.
- doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ doReturn(avoidBadWifi ? 1 : 0).when(mResources)
+ .getInteger(R.integer.config_networkAvoidBadWifi);
doReturn(preferBadWifi ? 1 : 0).when(mResources)
.getInteger(R.integer.config_activelyPreferBadWifi);
mPolicyTracker.reevaluate();
@@ -6067,7 +6089,9 @@
mWiFiAgent.connect(false);
wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
- if (preferBadWifi) {
+ mDeps.mScheduledEvaluationTimeouts.poll(TIMEOUT_MS, t -> checkUnvalidationTimeout.test(t));
+
+ if (!avoidBadWifi && preferBadWifi) {
expectUnvalidationCheckWillNotify(mWiFiAgent, NotificationType.LOST_INTERNET);
mDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
} else {
@@ -6077,15 +6101,31 @@
}
@Test
- public void testPreferBadWifi_doNotPrefer() throws Exception {
+ public void testPreferBadWifi_doNotAvoid_doNotPrefer() throws Exception {
// Starting with U this mode is no longer supported and can't actually be tested
assumeFalse(SdkLevel.isAtLeastU());
- doTestPreferBadWifi(false /* preferBadWifi */);
+ doTestPreferBadWifi(false /* avoidBadWifi */, false /* preferBadWifi */,
+ timeout -> timeout < 14_000);
}
@Test
- public void testPreferBadWifi_doPrefer() throws Exception {
- doTestPreferBadWifi(true /* preferBadWifi */);
+ public void testPreferBadWifi_doNotAvoid_doPrefer() throws Exception {
+ doTestPreferBadWifi(false /* avoidBadWifi */, true /* preferBadWifi */,
+ timeout -> timeout > 14_000);
+ }
+
+ @Test
+ public void testPreferBadWifi_doAvoid_doNotPrefer() throws Exception {
+ // If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
+ doTestPreferBadWifi(true /* avoidBadWifi */, false /* preferBadWifi */,
+ timeout -> timeout < 14_000);
+ }
+
+ @Test
+ public void testPreferBadWifi_doAvoid_doPrefer() throws Exception {
+ // If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
+ doTestPreferBadWifi(true /* avoidBadWifi */, true /* preferBadWifi */,
+ timeout -> timeout < 14_000);
}
@Test
@@ -10276,7 +10316,7 @@
private void doTestSetFirewallChainEnabledCloseSocket(final int chain,
final boolean isAllowList) throws Exception {
- reset(mDeps);
+ reset(mDestroySocketsWrapper);
mCm.setFirewallChainEnabled(chain, true /* enabled */);
final Set<Integer> uids =
@@ -10284,13 +10324,13 @@
if (isAllowList) {
final Set<Range<Integer>> range = new ArraySet<>(
List.of(new Range<>(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE)));
- verify(mDeps).destroyLiveTcpSockets(range, uids);
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(range, uids);
} else {
- verify(mDeps).destroyLiveTcpSocketsByOwnerUids(uids);
+ verify(mDestroySocketsWrapper).destroyLiveTcpSocketsByOwnerUids(uids);
}
mCm.setFirewallChainEnabled(chain, false /* enabled */);
- verifyNoMoreInteractions(mDeps);
+ verifyNoMoreInteractions(mDestroySocketsWrapper);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
@@ -12627,11 +12667,11 @@
private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
throws Exception {
- InOrder inOrder = inOrder(mMockNetd, mDeps);
+ InOrder inOrder = inOrder(mMockNetd, mDestroySocketsWrapper);
final Set<Integer> exemptUidSet = new ArraySet<>(List.of(exemptUid, Process.VPN_UID));
- inOrder.verify(mDeps).destroyLiveTcpSockets(UidRange.toIntRanges(vpnRanges),
- exemptUidSet);
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
if (add) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(
@@ -12643,8 +12683,8 @@
toUidRangeStableParcels(vpnRanges), PREFERENCE_ORDER_VPN));
}
- inOrder.verify(mDeps).destroyLiveTcpSockets(UidRange.toIntRanges(vpnRanges),
- exemptUidSet);
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
}
@Test
@@ -17984,7 +18024,7 @@
final UidRange frozenUidRange = new UidRange(TEST_FROZEN_UID, TEST_FROZEN_UID);
final Set<UidRange> ranges = Collections.singleton(frozenUidRange);
- verify(mDeps).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
eq(exemptUids));
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 395e2bb..2d2819c 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -25,6 +25,9 @@
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -40,7 +43,6 @@
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV4;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV6;
-import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.UserHandle.PER_USER_RANGE;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
@@ -55,7 +57,6 @@
import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_ESP;
import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_UDP;
import static com.android.testutils.Cleanup.testAndCleanup;
-import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
@@ -166,6 +167,7 @@
import android.util.Pair;
import android.util.Range;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -173,12 +175,12 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.DeviceIdleInternal;
import com.android.server.IpSecService;
import com.android.server.VpnTestBase;
import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Rule;
@@ -196,6 +198,7 @@
import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -213,7 +216,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Tests for {@link Vpn}.
@@ -221,9 +225,8 @@
* Build, install and run with:
* runtest frameworks-net -c com.android.server.connectivity.VpnTest
*/
-@RunWith(DevSdkIgnoreRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
-@IgnoreUpTo(S_V2)
public class VpnTest extends VpnTestBase {
private static final String TAG = "VpnTest";
@@ -2608,6 +2611,81 @@
vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
}
+ private String getDump(@NonNull final Vpn vpn) {
+ final StringWriter sw = new StringWriter();
+ final IndentingPrintWriter writer = new IndentingPrintWriter(sw, "");
+ vpn.dump(writer);
+ writer.flush();
+ return sw.toString();
+ }
+
+ private int countMatches(@NonNull final Pattern regexp, @NonNull final String string) {
+ final Matcher m = regexp.matcher(string);
+ int i = 0;
+ while (m.find()) ++i;
+ return i;
+ }
+
+ @Test
+ public void testNCEventChanges() throws Exception {
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .setLinkDownstreamBandwidthKbps(1000)
+ .setLinkUpstreamBandwidthKbps(500);
+
+ final Ikev2VpnProfile ikeProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .setBypassable(true /* isBypassable */)
+ .setAutomaticNattKeepaliveTimerEnabled(true)
+ .setAutomaticIpVersionSelectionEnabled(true)
+ .build();
+
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ ncBuilder.build(), false /* mtuSupportsIpv6 */,
+ true /* areLongLivedTcpConnectionsExpensive */);
+
+ // Calls to onCapabilitiesChanged will be thrown to the executor for execution ; by
+ // default this will incur a 10ms delay before it's executed, messing with the timing
+ // of the log and having the checks for counts in equals() below flake.
+ mExecutor.executeDirect = true;
+
+ // First nc changed triggered by verifySetupPlatformVpn
+ final Pattern pattern = Pattern.compile("Cap changed from", Pattern.MULTILINE);
+ final String stage1 = getDump(vpnSnapShot.vpn);
+ assertEquals(1, countMatches(pattern, stage1));
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage2 = getDump(vpnSnapShot.vpn);
+ // Was the same caps, there should still be only 1 match
+ assertEquals(1, countMatches(pattern, stage2));
+
+ ncBuilder.setLinkDownstreamBandwidthKbps(1200)
+ .setLinkUpstreamBandwidthKbps(300);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage3 = getDump(vpnSnapShot.vpn);
+ // Was not an important change, should not be logged, still only 1 match
+ assertEquals(1, countMatches(pattern, stage3));
+
+ ncBuilder.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage4 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match
+ assertEquals(2, countMatches(pattern, stage4));
+
+ ncBuilder.removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ ncBuilder.setLinkDownstreamBandwidthKbps(600);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage5 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match even with the unimportant change
+ assertEquals(3, countMatches(pattern, stage5));
+ }
+ // TODO : beef up event logs tests
+
private void verifyHandlingNetworkLoss(PlatformVpnSnapshot vpnSnapShot) throws Exception {
// Forget the #sendLinkProperties during first setup.
reset(mMockNetworkAgent);
@@ -3105,30 +3183,4 @@
} catch (Exception e) {
}
}
-
- private void setMockedNetworks(final Map<Network, NetworkCapabilities> networks) {
- doAnswer(invocation -> {
- final Network network = (Network) invocation.getArguments()[0];
- return networks.get(network);
- }).when(mConnectivityManager).getNetworkCapabilities(any());
- }
-
- // Need multiple copies of this, but Java's Stream objects can't be reused or
- // duplicated.
- private Stream<String> publicIpV4Routes() {
- return Stream.of(
- "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4",
- "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6",
- "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9",
- "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11",
- "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14",
- "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7",
- "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4");
- }
-
- private Stream<String> publicIpV6Routes() {
- return Stream.of(
- "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6",
- "fe00::/8", "2605:ef80:e:af1d::/64");
- }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
new file mode 100644
index 0000000..c62a081
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
@@ -0,0 +1,90 @@
+package com.android.server.connectivity.mdns.internal
+
+import android.net.LinkAddress
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.system.OsConstants
+import com.android.net.module.util.SharedLog
+import com.android.net.module.util.netlink.NetlinkConstants
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage
+import com.android.net.module.util.netlink.StructIfaddrMsg
+import com.android.net.module.util.netlink.StructNlMsgHdr
+import com.android.server.connectivity.mdns.MdnsAdvertiserTest
+import com.android.server.connectivity.mdns.MdnsSocketProvider
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+private val LINKADDRV4 = LinkAddress("192.0.2.0/24")
+private val IFACE_IDX = 32
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+internal class SocketNetlinkMonitorTest {
+ private val thread = HandlerThread(MdnsAdvertiserTest::class.simpleName)
+ private val sharedlog = Mockito.mock(SharedLog::class.java)
+ private val netlinkMonitorCallBack =
+ Mockito.mock(MdnsSocketProvider.NetLinkMonitorCallBack::class.java)
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ @Test
+ fun testHandleDeprecatedNetlinkMessage() {
+ val socketNetlinkMonitor = SocketNetlinkMonitor(Handler(thread.looper), sharedlog,
+ netlinkMonitorCallBack)
+ val nlmsghdr = StructNlMsgHdr().apply {
+ nlmsg_type = NetlinkConstants.RTM_NEWADDR
+ nlmsg_flags = (StructNlMsgHdr.NLM_F_REQUEST.toInt()
+ or StructNlMsgHdr.NLM_F_ACK.toInt()).toShort()
+ nlmsg_seq = 1
+ }
+ val structIfaddrMsg = StructIfaddrMsg(OsConstants.AF_INET.toShort(),
+ LINKADDRV4.prefixLength.toShort(),
+ LINKADDRV4.flags.toShort(),
+ LINKADDRV4.scope.toShort(), IFACE_IDX)
+ // If the LinkAddress is not preferred, RTM_NEWADDR will not trigger
+ // addOrUpdateInterfaceAddress() callback.
+ val deprecatedAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+ socketNetlinkMonitor.processNetlinkMessage(deprecatedAddNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack, never()).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+
+ // If the LinkAddress is preferred, RTM_NEWADDR will trigger addOrUpdateInterfaceAddress()
+ // callback.
+ val preferredAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_OPTIMISTIC)
+ socketNetlinkMonitor.processNetlinkMessage(preferredAddNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+
+ // Even if the LinkAddress is not preferred, RTM_DELADDR will trigger
+ // deleteInterfaceAddress() callback.
+ nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR
+ val deprecatedDelNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+ socketNetlinkMonitor.processNetlinkMessage(deprecatedDelNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack).deleteInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+ }
+}