Merge "Connectivity: remove usages of deprecated Mockito matchers" into main
diff --git a/Tethering/apex/canned_fs_config b/Tethering/apex/canned_fs_config
index 1f5fcfa..edc5515 100644
--- a/Tethering/apex/canned_fs_config
+++ b/Tethering/apex/canned_fs_config
@@ -1,3 +1,3 @@
-/bin/for-system 0 1000 0750
+/bin/for-system 1029 1000 0750
/bin/for-system/clatd 1029 1029 06755
/bin/netbpfload 0 0 0750
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 2f9c3bc..a8a471d 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -340,6 +340,11 @@
* @hide
*/
public static final int TETHER_ERROR_BLUETOOTH_SERVICE_PENDING = 19;
+ /**
+ * Never used outside Tethering.java.
+ * @hide
+ */
+ public static final int TETHER_ERROR_SOFT_AP_CALLBACK_PENDING = 20;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/Tethering/src/com/android/networkstack/tethering/RequestTracker.java b/Tethering/src/com/android/networkstack/tethering/RequestTracker.java
index 9c61716..c91ff58 100644
--- a/Tethering/src/com/android/networkstack/tethering/RequestTracker.java
+++ b/Tethering/src/com/android/networkstack/tethering/RequestTracker.java
@@ -240,6 +240,11 @@
mServingRequests.entrySet().removeIf(e -> e.getValue().getTetheringType() == type);
}
+ @VisibleForTesting
+ List<TetheringRequest> getServingTetheringRequests() {
+ return new ArrayList<>(mServingRequests.values());
+ }
+
/**
* Returns an existing (pending or serving) request that fuzzy matches the given request.
* Optionally specify matchUid to only return requests with the same uid.
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index c7ae353..0cf008b 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -47,6 +47,7 @@
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.TetheringManager.TETHER_ERROR_SOFT_AP_CALLBACK_PENDING;
import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_REQUEST;
@@ -114,6 +115,7 @@
import android.net.TetheringManager.TetheringRequest;
import android.net.Uri;
import android.net.ip.IpServer;
+import android.net.wifi.SoftApState;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pGroup;
@@ -775,7 +777,7 @@
final int result;
switch (type) {
case TETHERING_WIFI:
- result = setWifiTethering(enable);
+ result = setWifiTethering(enable, request, listener);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
@@ -800,6 +802,9 @@
// The result of Bluetooth tethering will be sent after the pan service connects.
if (result == TETHER_ERROR_BLUETOOTH_SERVICE_PENDING) return;
+ // The result of Wifi tethering will be sent after the SoftApCallback result.
+ if (result == TETHER_ERROR_SOFT_AP_CALLBACK_PENDING) return;
+
sendTetherResultAndRemoveOnError(request, listener, result);
}
@@ -824,7 +829,8 @@
}
}
- private int setWifiTethering(final boolean enable) {
+ private int setWifiTethering(final boolean enable, TetheringRequest request,
+ IIntResultListener listener) {
final long ident = Binder.clearCallingIdentity();
try {
final WifiManager mgr = getWifiManager();
@@ -832,8 +838,34 @@
mLog.e("setWifiTethering: failed to get WifiManager!");
return TETHER_ERROR_SERVICE_UNAVAIL;
}
- if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))
- || (!enable && mgr.stopSoftAp())) {
+ final boolean success;
+ if (enable) {
+ if (isTetheringWithSoftApConfigEnabled()) {
+ // Notes:
+ // - A call to startTetheredHotspot can only succeed if the SoftAp is idle. If
+ // the SoftAp is running or is being disabled, the call will fail.
+ // - If a call to startTetheredHotspot fails, the callback is immediately called
+ // with WIFI_AP_STATE_FAILED and a null interface.
+ // - If a call to startTetheredHotspot succeeds, the passed-in callback is the
+ // only callback that will receive future WIFI_AP_STATE_ENABLED and
+ // WIFI_AP_STATE_DISABLED events in the future, until another call to
+ // startTetheredHotspot succeeds, at which point the old callback will stop
+ // receiving any events.
+ // - Wifi may decide to restart the hotspot at any time (such as for a CC
+ // change), and if it does so, it will send WIFI_AP_STATE_DISABLED and then
+ // either WIFI_AP_STATE_ENABLED or (if restarting fails) WIFI_AP_STATE_FAILED.
+ mgr.startTetheredHotspot(request, mExecutor,
+ new StartTetheringSoftApCallback(listener));
+ // Result isn't used since we get the real result via
+ // StartTetheringSoftApCallback.
+ return TETHER_ERROR_SOFT_AP_CALLBACK_PENDING;
+ }
+ success = mgr.startTetheredHotspot(null);
+ } else {
+ success = mgr.stopSoftAp();
+ }
+
+ if (success) {
return TETHER_ERROR_NO_ERROR;
}
} finally {
@@ -1458,6 +1490,9 @@
final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED);
+ // In B+, Tethered AP is handled by StartTetheringSoftApCallback.
+ if (isTetheringWithSoftApConfigEnabled() && ipmode == IFACE_IP_MODE_TETHERED) return;
+
switch (curState) {
case WifiManager.WIFI_AP_STATE_ENABLING:
// We can see this state on the way to both enabled and failure states.
@@ -1538,12 +1573,64 @@
}
}
+ class StartTetheringSoftApCallback implements SoftApCallback {
+
+ @Nullable
+ IIntResultListener mPendingListener;
+
+ StartTetheringSoftApCallback(IIntResultListener pendingListener) {
+ mPendingListener = pendingListener;
+ }
+
+ @Override
+ public void onStateChanged(SoftApState softApState) {
+ final int state = softApState.getState();
+ final String iface = softApState.getIface();
+ final TetheringRequest request = softApState.getTetheringRequest();
+ switch (softApState.getState()) {
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ enableIpServing(request, iface);
+ // If stopTethering has already been called, IP serving will still be started,
+ // but as soon as the wifi code processes the stop, WIFI_AP_STATE_DISABLED will
+ // be sent and tethering will be stopped again.
+ sendTetherResultAndRemoveOnError(request, mPendingListener,
+ TETHER_ERROR_NO_ERROR);
+ mPendingListener = null;
+ break;
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ // TODO: if a call to startTethering happens just after a call to stopTethering,
+ // the start will fail because hotspot is still being disabled. This likely
+ // cannot be fixed in tethering code but must be fixed in WiFi.
+ sendTetherResultAndRemoveOnError(request, mPendingListener,
+ TETHER_ERROR_INTERNAL_ERROR);
+ mPendingListener = null;
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ // TODO(b/403164072): SoftAP may restart due to CC change, in which we'll get
+ // DISABLED -> ENABLED (or FAILED). Before the transition back to ENABLED is
+ // complete, it is possible that a new Wifi request is accepted since there's no
+ // active request to fuzzy-match it, which will unexpectedly cause Wifi to
+ // overwrite this SoftApCallback. This should be fixed in Wifi to disallow any
+ // new calls to startTetheredHotspot while SoftAP is restarting.
+ disableWifiIpServing(iface, state);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
@VisibleForTesting
List<TetheringRequest> getPendingTetheringRequests() {
return mRequestTracker.getPendingTetheringRequests();
}
@VisibleForTesting
+ List<TetheringRequest> getServingTetheringRequests() {
+ return mRequestTracker.getServingTetheringRequests();
+ }
+
+ @VisibleForTesting
boolean isTetheringActive() {
return getTetheredIfaces().length > 0;
}
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 a69ead8..0707e20 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -69,6 +69,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -169,6 +170,7 @@
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.SoftApState;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.SoftApCallback;
@@ -241,6 +243,7 @@
import java.util.List;
import java.util.Set;
import java.util.Vector;
+import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -842,6 +845,38 @@
mLooper.dispatchAll();
}
+ private void sendStartTetheringSoftApCallback(int state, TetheringRequest request,
+ String ifname) {
+ ArgumentCaptor<SoftApCallback> callbackCaptor =
+ ArgumentCaptor.forClass(SoftApCallback.class);
+ verify(mWifiManager, atLeastOnce()).startTetheredHotspot(any(TetheringRequest.class),
+ any(Executor.class), callbackCaptor.capture());
+ SoftApState softApState = mock(SoftApState.class);
+ when(softApState.getState()).thenReturn(state);
+ when(softApState.getTetheringRequest()).thenReturn(request);
+ when(softApState.getIface()).thenReturn(ifname);
+ callbackCaptor.getValue().onStateChanged(softApState);
+ mLooper.dispatchAll();
+ }
+
+ private void verifyWifiTetheringRequested() {
+ if (mTetheringDependencies.isTetheringWithSoftApConfigEnabled()) {
+ verify(mWifiManager).startTetheredHotspot(any(), any(), any());
+ } else {
+ verify(mWifiManager).startTetheredHotspot(null);
+ }
+ verify(mWifiManager, never()).stopSoftAp();
+ verifyNoMoreInteractions(mWifiManager);
+ }
+
+ private void sendSoftApEvent(int state, TetheringRequest request, String ifname) {
+ if (mTetheringDependencies.isTetheringWithSoftApConfigEnabled()) {
+ sendStartTetheringSoftApCallback(state, request, ifname);
+ } else {
+ sendWifiApStateChanged(state, ifname, IFACE_IP_MODE_TETHERED);
+ }
+ }
+
private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = {
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_WIFI_STATE
@@ -1964,11 +1999,9 @@
initTetheringOnTestThread();
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
- null);
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG, null);
mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
+ verifyWifiTetheringRequested();
verifyNoMoreInteractions(mNetd);
// Emulate externally-visible WifiManager effects, causing the
@@ -1997,14 +2030,15 @@
// TODO: Test with and without interfaceStatusChanged().
@Test
public void workingWifiTetheringEnrichedApBroadcast() throws Exception {
+ // B+ uses SoftApCallback instead of WIFI_AP_STATE_CHANGED for tethered hotspot.
+ mTetheringWithSoftApConfigEnabled = false;
initTetheringOnTestThread();
// Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
+ verifyWifiTetheringRequested();
verifyNoMoreInteractions(mNetd);
// Emulate externally-visible WifiManager effects, causing the
@@ -2042,6 +2076,122 @@
verifyStopHotpot();
}
+ @Test
+ public void startWifiTetheringWithSoftApConfigurationSuccess() throws Exception {
+ assumeTrue(mTetheringDependencies.isTetheringWithSoftApConfigEnabled());
+ initTetheringOnTestThread();
+
+ // Emulate pressing the WiFi tethering button.
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("SSID".getBytes(StandardCharsets.UTF_8)))
+ .build())
+ .build();
+ IIntResultListener startResultListener = mock(IIntResultListener.class);
+ mTethering.startTethering(request, TEST_CALLER_PKG, startResultListener);
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mNetd);
+ verify(startResultListener, never()).onResult(anyInt());
+ // Emulate Wifi iface enabled
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
+
+ verifyStartHotspot();
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
+ verify(startResultListener).onResult(TETHER_ERROR_NO_ERROR);
+ }
+
+ @Test
+ public void startWifiTetheringWithSoftApConfigurationFailure() throws Exception {
+ assumeTrue(mTetheringDependencies.isTetheringWithSoftApConfigEnabled());
+ initTetheringOnTestThread();
+
+ // Emulate pressing the WiFi tethering button.
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("SSID".getBytes(StandardCharsets.UTF_8)))
+ .build())
+ .build();
+ IIntResultListener startResultListener = mock(IIntResultListener.class);
+ mTethering.startTethering(request, TEST_CALLER_PKG, startResultListener);
+ mLooper.dispatchAll();
+ verify(startResultListener, never()).onResult(anyInt());
+ // Emulate Wifi iface failure
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_FAILED, request, TEST_WLAN_IFNAME);
+
+ verify(startResultListener).onResult(TETHER_ERROR_INTERNAL_ERROR);
+ }
+
+ @Test
+ public void startWifiTetheringWithSoftApConfigurationRestartAfterStarting() throws Exception {
+ assumeTrue(mTetheringDependencies.isTetheringWithSoftApConfigEnabled());
+ initTetheringOnTestThread();
+ TestTetheringEventCallback callback = new TestTetheringEventCallback();
+ SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("SSID".getBytes(StandardCharsets.UTF_8)))
+ .build();
+ final TetheringInterface wifiIface = new TetheringInterface(
+ TETHERING_WIFI, TEST_WLAN_IFNAME);
+ final TetheringInterface wifiIfaceWithConfig = new TetheringInterface(
+ TETHERING_WIFI, TEST_WLAN_IFNAME, softApConfig);
+
+ // 1. Register one callback before running any tethering.
+ mTethering.registerTetheringEventCallback(callback);
+ mLooper.dispatchAll();
+ assertTetherStatesNotNullButEmpty(callback.pollTetherStatesChanged());
+ // Emulate pressing the WiFi tethering button.
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(softApConfig)
+ .build();
+ IIntResultListener startResultListener = mock(IIntResultListener.class);
+ mTethering.startTethering(request, TEST_CALLER_PKG, startResultListener);
+ mLooper.dispatchAll();
+
+ // Wifi success
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
+ verifyStartHotspot();
+ TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIfaceWithConfig});
+ verify(startResultListener).onResult(TETHER_ERROR_NO_ERROR);
+
+ // Restart Wifi
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_DISABLED, request, TEST_WLAN_IFNAME);
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
+
+ // Verify we go from TETHERED -> AVAILABLE -> TETHERED with the same config.
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIfaceWithConfig});
+ }
+
+ @Test
+ public void startWifiApBroadcastDoesNotStartIpServing() throws Exception {
+ assumeTrue(mTetheringDependencies.isTetheringWithSoftApConfigEnabled());
+ initTetheringOnTestThread();
+
+ // Call startTethering for wifi
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("SSID".getBytes(StandardCharsets.UTF_8)))
+ .build())
+ .build();
+ IIntResultListener startResultListener = mock(IIntResultListener.class);
+ mTethering.startTethering(request, TEST_CALLER_PKG, startResultListener);
+ mLooper.dispatchAll();
+
+ // WIFI_AP_STATE_CHANGED broadcast should be ignored since we should only be using
+ // SoftApCallback for tethered AP.
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ verify(mNetd, never()).tetherStartWithConfiguration(any());
+ verify(mNotificationUpdater, never()).onDownstreamChanged(DOWNSTREAM_NONE);
+ verify(mWifiManager, never()).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ assertTrue(mTethering.getServingTetheringRequests().isEmpty());
+ }
+
// TODO: Test with and without interfaceStatusChanged().
@Test
public void failureEnablingIpForwarding() throws Exception {
@@ -2049,11 +2199,10 @@
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
- null);
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
+ verifyWifiTetheringRequested();
verifyNoMoreInteractions(mNetd);
verify(mTetheringMetrics).createBuilder(eq(TETHERING_WIFI), anyString());
@@ -2061,7 +2210,7 @@
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
// We verify get/set called three times here: twice for setup and once during
// teardown because all events happen over the course of the single
@@ -2390,7 +2539,8 @@
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
initTetheringUpstream(upstreamState);
- mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG,
null);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
mLooper.dispatchAll();
@@ -2398,7 +2548,7 @@
// Starting in B, ignore the interfaceStatusChanged
callback.assertNoStateChangeCallback();
}
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
@@ -2426,8 +2576,7 @@
if (isAtLeastT()) {
// After T, tethering doesn't support WIFI_AP_STATE_DISABLED with null interface name.
callback2.assertNoStateChangeCallback();
- sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME,
- IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_DISABLED, request, TEST_WLAN_IFNAME);
}
tetherState = callback2.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
@@ -2500,7 +2649,8 @@
mLooper.dispatchAll();
// Netd "up" event should not trigger a state change callback in B+.
callback.assertNoStateChangeCallback();
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest,
+ TEST_WLAN_IFNAME);
// Verify we see Available -> Tethered states
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
callback.pollTetherStatesChanged().availableList);
@@ -2520,8 +2670,8 @@
// Disable wifi tethering
mTethering.stopTethering(TETHERING_WIFI);
mLooper.dispatchAll();
- sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME,
- IFACE_IP_MODE_TETHERED);
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_DISABLED, tetheringRequest,
+ TEST_WLAN_IFNAME);
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
callback.pollTetherStatesChanged().availableList);
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
@@ -2566,7 +2716,7 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
- successListener.assertHasResult();
+ successListener.assertDoesNotHaveResult();
// Try starting wifi tethering with various fuzzy-matching requests and verify we get
// TETHER_ERROR_DUPLICATE_REQUEST.
@@ -2581,8 +2731,7 @@
ResultListener differentIpAddrListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
mTethering.startTethering(differentIpAddr, TEST_CALLER_PKG, differentIpAddrListener);
mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(any());
- verify(mWifiManager, never()).stopSoftAp();
+ verifyWifiTetheringRequested();
differentIpAddrListener.assertHasResult();
// Different UID
@@ -2593,13 +2742,13 @@
mTethering.startTethering(differentUid, TEST_CALLER_PKG, differentUidListener);
mLooper.dispatchAll();
differentUidListener.assertHasResult();
- verify(mWifiManager, times(1)).startTetheredHotspot(any());
- verify(mWifiManager, never()).stopSoftAp();
+ verifyWifiTetheringRequested();
- // Mock the link layer event to start IP serving and verify we still get
- // TETHER_ERROR_DUPLICATE_REQUEST even though the request is no longer pending and is
- // already serving.
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ // Mock the link layer event to start IP serving and verify
+ // 1) The original request's result listener is called.
+ // 2) We still get TETHER_ERROR_DUPLICATE_REQUEST for new requests.
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
+ successListener.assertHasResult();
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
callback.pollTetherStatesChanged().availableList);
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
@@ -2613,7 +2762,7 @@
mLooper.dispatchAll();
differentIpAddrListener.assertHasResult();
differentUidListener.assertHasResult();
- verify(mWifiManager, times(1)).startTetheredHotspot(any());
+ verify(mWifiManager, times(1)).startTetheredHotspot(any(), any(), any());
verify(mWifiManager, never()).stopSoftAp();
}
@@ -2630,8 +2779,8 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener.assertHasResult();
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
@@ -2662,9 +2811,8 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener.assertHasResult();
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
-
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, failureListener);
@@ -2672,12 +2820,15 @@
failureListener.assertHasResult();
// Trigger wifi ap state change to tell IpServer it's unwanted.
- sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_DISABLED, tetheringRequest,
+ TEST_WLAN_IFNAME);
// We should be able to request the same Wifi again
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest,
+ TEST_WLAN_IFNAME);
successListener2.assertHasResult();
}
@@ -2697,14 +2848,15 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener.assertHasResult();
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
// We should be able to request the same Wifi again since the DHCP server transitioned the
// IpServer back to InitialState.
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener2.assertHasResult();
}
@@ -2721,7 +2873,10 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
- successListener.assertHasResult();
+ ArgumentCaptor<SoftApCallback> callbackCaptor =
+ ArgumentCaptor.forClass(SoftApCallback.class);
+ verify(mWifiManager, atLeastOnce()).startTetheredHotspot(any(TetheringRequest.class),
+ any(Executor.class), callbackCaptor.capture());
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
@@ -2736,6 +2891,20 @@
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
+ successListener2.assertHasResult();
+
+ // Mock the first request going up and then down from the stop request.
+ SoftApState softApState = mock(SoftApState.class);
+ when(softApState.getState()).thenReturn(WIFI_AP_STATE_ENABLED);
+ when(softApState.getTetheringRequest()).thenReturn(tetheringRequest);
+ when(softApState.getIface()).thenReturn(TEST_WLAN_IFNAME);
+ callbackCaptor.getValue().onStateChanged(softApState);
+ mLooper.dispatchAll();
+ successListener.assertHasResult();
+
+ // Mock the second request going up
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener2.assertHasResult();
}
@@ -2752,8 +2921,8 @@
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener.assertHasResult();
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
@@ -2768,6 +2937,7 @@
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
mLooper.dispatchAll();
+ sendStartTetheringSoftApCallback(WIFI_AP_STATE_ENABLED, tetheringRequest, TEST_WLAN_IFNAME);
successListener2.assertHasResult();
}
@@ -3216,7 +3386,7 @@
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL, null),
TEST_CALLER_PKG, null);
mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(any());
+ verifyWifiTetheringRequested();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
// Call legacyTether on the interface before the link layer event comes back.
@@ -3588,8 +3758,11 @@
reset(mDhcpServer);
// Run wifi tethering.
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
+ mLooper.dispatchAll();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), dhcpEventCbsCaptor.capture());
eventCallbacks = dhcpEventCbsCaptor.getValue();
@@ -3652,8 +3825,12 @@
});
callback.expectTetheredClientChanged(Collections.emptyList());
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
+ mLooper.dispatchAll();
+ verifyWifiTetheringRequested();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor =
ArgumentCaptor.forClass(IDhcpEventCallbacks.class);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
@@ -4184,8 +4361,11 @@
@Test
public void testIpv4AddressForSapAndLohsConcurrency() throws Exception {
initTetheringOnTestThread();
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
+ mLooper.dispatchAll();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
@@ -4234,14 +4414,13 @@
assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastErrorForTest(TEST_P2P_IFNAME));
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
- null);
+ TetheringRequest request = createTetheringRequest(TETHERING_WIFI);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
- verify(mWifiManager).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
+ verifyWifiTetheringRequested();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ sendSoftApEvent(WIFI_AP_STATE_ENABLED, request, TEST_WLAN_IFNAME);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
diff --git a/bpf/progs/netd.c b/bpf/progs/netd.c
index 08635b3..b146e45 100644
--- a/bpf/progs/netd.c
+++ b/bpf/progs/netd.c
@@ -597,7 +597,7 @@
// V | | | x | x | x | x | x | x | | (netbpfload)
// U | | x | x | x | x | x | x | | |
// T | x | x | x | x | x | x | | | | (magic netbpfload)
-// S | x | x | x | x | x | | | | | (platform loads offload)
+// S | x | x | x | x | x | | | | | (dns netbpfload for offload)
// R | x | x | x | x | | | | | | (no mainline ebpf)
//
// Not relevant for eBPF, but R can also run on 4.4
diff --git a/clatd/clatd.c b/clatd/clatd.c
index bac8b1d..9b1d987 100644
--- a/clatd/clatd.c
+++ b/clatd/clatd.c
@@ -49,7 +49,8 @@
struct clat_config Global_Clatd_Config;
-volatile sig_atomic_t running = 1;
+volatile sig_atomic_t sigterm = 0;
+bool running = true;
// reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun
void process_packet_6_to_4(struct tun_data *tunnel) {
@@ -78,10 +79,11 @@
if (errno != EAGAIN) {
logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
}
+ if (errno == ENETDOWN) running = false;
return;
} else if (readlen == 0) {
logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__);
- running = 0;
+ running = false;
return;
} else if (readlen >= sizeof(buf)) {
logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
@@ -161,10 +163,11 @@
if (errno != EAGAIN) {
logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
}
+ if (errno == ENETDOWN) running = false; // not sure if this can happen
return;
} else if (readlen == 0) {
logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__);
- running = 0;
+ running = false;
return;
} else if (readlen >= sizeof(buf)) {
logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
@@ -273,23 +276,12 @@
* tunnel - tun device data
*/
void event_loop(struct tun_data *tunnel) {
- // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
- // this would then result in an ipv6-only network with working native ipv6, working
- // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
- // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
- // So we'll spoof dad here, and yeah, we really should check for a response and in
- // case of failure pick a different IP. Seeing as 48-bits of the IP are utterly random
- // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
- // concern...
- // TODO: actually perform true DAD
- send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
-
struct pollfd wait_fd[] = {
{ tunnel->read_fd6, POLLIN, 0 },
{ tunnel->fd4, POLLIN, 0 },
};
- while (running) {
+ while (running && !sigterm) {
if (poll(wait_fd, ARRAY_SIZE(wait_fd), -1) == -1) {
if (errno != EINTR) {
logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno));
diff --git a/clatd/clatd.h b/clatd/clatd.h
index e170c58..daa5ebc 100644
--- a/clatd/clatd.h
+++ b/clatd/clatd.h
@@ -52,7 +52,9 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-extern volatile sig_atomic_t running;
+extern volatile sig_atomic_t sigterm;
+
+void send_dad(int fd, const struct in6_addr* tgt);
void event_loop(struct tun_data *tunnel);
diff --git a/clatd/main.c b/clatd/main.c
index 7aa1671..bc29041 100644
--- a/clatd/main.c
+++ b/clatd/main.c
@@ -34,10 +34,10 @@
#define DEVICEPREFIX "v4-"
-/* function: stop_loop
+/* function: handle_sigterm
* signal handler: stop the event loop
*/
-static void stop_loop(__attribute__((unused)) int unused) { running = 0; };
+static void handle_sigterm(__attribute__((unused)) int unused) { sigterm = 1; };
/* function: print_help
* in case the user is running this on the command line
@@ -64,6 +64,48 @@
*write_sock_str = NULL;
unsigned len;
+ // Clatd binary is setuid/gid CLAT, thus when we reach here we have:
+ // $ adb shell ps | grep clat
+ // [pid] [ppid]
+ // clat 7650 1393 10785364 2612 do_sys_poll 0 S clatd-wlan0
+ // $ adb shell cat /proc/7650/status | egrep -i '^(Uid:|Gid:|Groups:)'
+ // [real][effective][saved][filesystem]
+ // [uid] [euid] [suid] [fsuid]
+ // Uid: 1000 1029 1029 1029
+ // [gid] [egid] [sgid] [fsgid]
+ // Gid: 1000 1029 1029 1029
+ // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+ // This mismatch between uid & euid appears to cause periodic (every 5 minutes):
+ // objhash pid ppid uid
+ // W ActivityManager: Stale PhantomProcessRecord {xxxxxxx 7650:1393:clatd-wlan0/1000}, removing
+ // This is due to:
+ // $ adbz shell ls -ld /proc/7650
+ // dr-xr-xr-x 9 clat clat 0 2025-03-14 11:37 /proc/7650
+ // which is used by
+ // //frameworks/base/core/java/com/android/internal/os/ProcessCpuTracker.java
+ // which thus returns the uid 'clat' vs
+ // //frameworks/base/core/java/android/os/Process.java
+ // getUidForPid() which grabs *real* 'uid' from /proc/<pid>/status and is used in:
+ // //frameworks/base/services/core/java/com/android/server/am/PhantomProcessList.java
+ // (perhaps this should grab euid instead? unclear)
+ //
+ // However, we want to drop as many privs as possible, hence:
+ gid_t egid = getegid(); // documented to never fail, hence should return AID_CLAT == 1029
+ uid_t euid = geteuid(); // (ditto)
+ setresgid(egid, egid, egid); // ignore any failure
+ setresuid(euid, euid, euid); // ignore any failure
+ // ideally we'd somehow drop supplementary groups too...
+ // but for historical reasons that actually requires CAP_SETGID which we don't have
+ // (see man 2 setgroups)
+ //
+ // Now we (should) have:
+ // $ adb shell ps | grep clat
+ // clat 5370 1479 10785364 2528 do_sys_poll 0 S clatd-wlan0
+ // # adb shell cat /proc/5370/status | egrep -i '^(Uid:|Gid:|Groups:)'
+ // Uid: 1029 1029 1029 1029
+ // Gid: 1029 1029 1029 1029
+ // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+
while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) {
switch (opt) {
case 'i':
@@ -150,7 +192,7 @@
exit(1);
}
- logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s plat=%s v4=%s v6=%s", CLATD_VERSION,
+ logmsg(ANDROID_LOG_INFO, "Starting clat version " CLATD_VERSION " on %s plat=%s v4=%s v6=%s",
uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)",
v6_addr ? v6_addr : "(none)");
@@ -183,25 +225,35 @@
}
// Loop until someone sends us a signal or brings down the tun interface.
- if (signal(SIGTERM, stop_loop) == SIG_ERR) {
+ if (signal(SIGTERM, handle_sigterm) == SIG_ERR) {
logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
exit(1);
}
+ // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
+ // this would then result in an ipv6-only network with working native ipv6, working
+ // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
+ // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
+ // So we'll spoof dad here, and yeah, we really should check for a response and in
+ // case of failure pick a different IP. Seeing as 48-bits of the IP are utterly random
+ // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
+ // concern...
+ // TODO: actually perform true DAD
+ send_dad(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
event_loop(&tunnel);
- logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface);
-
- if (running) {
- logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface);
+ if (sigterm) {
+ logmsg(ANDROID_LOG_INFO, "Shutting down clatd on %s, already received SIGTERM", uplink_interface);
+ } else {
+ // this implies running == false, ie. we received EOF or ENETDOWN error.
+ logmsg(ANDROID_LOG_INFO, "Shutting down clatd on %s, waiting for SIGTERM", uplink_interface);
// let's give higher level java code 15 seconds to kill us,
// but eventually terminate anyway, in case system server forgets about us...
- // sleep() should be interrupted by SIGTERM, the handler should clear running
+ // sleep() should be interrupted by SIGTERM, the handler should set 'sigterm'
sleep(15);
logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
- running ? "timed out waiting for" : "received");
- } else {
- logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface);
+ sigterm ? "received" : "timed out waiting for");
}
return 0;
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 3d7ea69..4f18fa2 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -1252,9 +1252,13 @@
* {@link ConnectivityManager#registerNetworkAgent}
* @hide
*/
- public static Network registerNetworkAgentResult(
+ public static NetworkAndAgentRegistryParcelable registerNetworkAgentResult(
@Nullable final Network network, @Nullable final INetworkAgentRegistry registry) {
- return network;
+ final NetworkAndAgentRegistryParcelable result =
+ new NetworkAndAgentRegistryParcelable();
+ result.network = network;
+ result.registry = registry;
+ return result;
}
}
@@ -3968,7 +3972,8 @@
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_FACTORY})
- public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni,
+ public NetworkAndAgentRegistryParcelable registerNetworkAgent(
+ @NonNull INetworkAgent na, @NonNull NetworkInfo ni,
@NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
@NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId) {
return registerNetworkAgent(na, ni, lp, nc, null /* localNetworkConfig */, score, config,
@@ -3983,7 +3988,8 @@
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_FACTORY})
- public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni,
+ public NetworkAndAgentRegistryParcelable registerNetworkAgent(
+ @NonNull INetworkAgent na, @NonNull NetworkInfo ni,
@NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
@Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score,
@NonNull NetworkAgentConfig config, int providerId) {
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 47b3316..a270684 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -30,6 +30,7 @@
import android.net.LocalNetworkConfig;
import android.net.Network;
import android.net.NetworkAgentConfig;
+import android.net.NetworkAndAgentRegistryParcelable;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
@@ -146,7 +147,8 @@
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
- Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
+ NetworkAndAgentRegistryParcelable registerNetworkAgent(
+ in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
in NetworkCapabilities nc, in NetworkScore score,
in LocalNetworkConfig localNetworkConfig, in NetworkAgentConfig config,
in int factorySerialNumber);
diff --git a/framework/src/android/net/INetworkAgent.aidl b/framework/src/android/net/INetworkAgent.aidl
index fa5175c..c6beeca 100644
--- a/framework/src/android/net/INetworkAgent.aidl
+++ b/framework/src/android/net/INetworkAgent.aidl
@@ -26,7 +26,7 @@
* @hide
*/
oneway interface INetworkAgent {
- void onRegistered(in INetworkAgentRegistry registry);
+ void onRegistered();
void onDisconnected();
void onBandwidthUpdateRequested();
void onValidationStatusChanged(int validationStatus,
diff --git a/framework/src/android/net/INetworkAgentRegistry.aidl b/framework/src/android/net/INetworkAgentRegistry.aidl
index 61b27b5..afdd1ee 100644
--- a/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -30,7 +30,7 @@
* Interface for NetworkAgents to send network properties.
* @hide
*/
-oneway interface INetworkAgentRegistry {
+interface INetworkAgentRegistry {
void sendNetworkCapabilities(in NetworkCapabilities nc);
void sendLinkProperties(in LinkProperties lp);
// TODO: consider replacing this by "markConnected()" and removing
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index cefa1ea..08f5ecd 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -98,6 +98,7 @@
@Nullable
private volatile Network mNetwork;
+ // Null before the agent is registered
@Nullable
private volatile INetworkAgentRegistry mRegistry;
@@ -121,6 +122,8 @@
private NetworkInfo mNetworkInfo;
@NonNull
private final Object mRegisterLock = new Object();
+ // TODO : move the preconnected queue to the system server and remove this
+ private boolean mConnected = false;
/**
* The ID of the {@link NetworkProvider} that created this object, or
@@ -606,16 +609,16 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_AGENT_CONNECTED: {
- if (mRegistry != null) {
- log("Received new connection while already connected!");
- } else {
- if (VDBG) log("NetworkAgent fully connected");
- synchronized (mPreConnectedQueue) {
- final INetworkAgentRegistry registry = (INetworkAgentRegistry) msg.obj;
- mRegistry = registry;
+ // TODO : move the pre-connected queue to the system server, and remove
+ // handling this EVENT_AGENT_CONNECTED message.
+ synchronized (mPreConnectedQueue) {
+ if (mConnected) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG) log("NetworkAgent fully connected");
for (RegistryAction a : mPreConnectedQueue) {
try {
- a.execute(registry);
+ a.execute(mRegistry);
} catch (RemoteException e) {
Log.wtf(LOG_TAG, "Communication error with registry", e);
// Fall through
@@ -623,6 +626,7 @@
}
mPreConnectedQueue.clear();
}
+ mConnected = true;
}
break;
}
@@ -631,7 +635,7 @@
// let the client know CS is done with us.
onNetworkUnwanted();
synchronized (mPreConnectedQueue) {
- mRegistry = null;
+ mConnected = false;
}
break;
}
@@ -758,20 +762,32 @@
}
final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkAndAgentRegistryParcelable result;
if (mInitialConfiguration.localNetworkConfig == null) {
// Call registerNetworkAgent without localNetworkConfig argument to pass
// android.net.cts.NetworkAgentTest#testAgentStartsInConnecting in old cts
- mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+ result = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
new NetworkInfo(mInitialConfiguration.info),
mInitialConfiguration.properties, mInitialConfiguration.capabilities,
mInitialConfiguration.score, mInitialConfiguration.config, providerId);
} else {
- mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+ result = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
new NetworkInfo(mInitialConfiguration.info),
mInitialConfiguration.properties, mInitialConfiguration.capabilities,
mInitialConfiguration.localNetworkConfig, mInitialConfiguration.score,
mInitialConfiguration.config, providerId);
}
+ if (null == result && Process.isApplicationUid(Process.myUid())) {
+ // Let it slide in tests to allow mocking, since NetworkAndAgentRegistryParcelable
+ // is not public and can't be instantiated by CTS. The danger here is that if
+ // this happens in production for some reason the code will crash later instead
+ // of here. If this is a system app, it will still crash as expected.
+ Log.e(LOG_TAG, "registerNetworkAgent returned null. This agent will not work. "
+ + "Is ConnectivityManager a mock ?");
+ } else {
+ mNetwork = result.network;
+ mRegistry = result.registry;
+ }
mInitialConfiguration = null; // All this memory can now be GC'd
}
return mNetwork;
@@ -787,8 +803,8 @@
}
@Override
- public void onRegistered(@NonNull INetworkAgentRegistry registry) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_CONNECTED, registry));
+ public void onRegistered() {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_CONNECTED));
}
@Override
@@ -913,11 +929,13 @@
*
* @hide
*/
- public INetworkAgent registerForTest(final Network network) {
+ public INetworkAgent registerForTest(final Network network,
+ final INetworkAgentRegistry registry) {
log("Registering NetworkAgent for test");
synchronized (mRegisterLock) {
mNetwork = network;
mInitialConfiguration = null;
+ mRegistry = registry;
}
return new NetworkAgentBinder(mHandler);
}
@@ -958,7 +976,7 @@
FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_MESSAGE_QUEUED_BEFORE_CONNECT
);
}
- if (mRegistry != null) {
+ if (mConnected) {
try {
action.execute(mRegistry);
} catch (RemoteException e) {
diff --git a/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl b/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl
new file mode 100644
index 0000000..8c01bbc
--- /dev/null
+++ b/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.INetworkAgentRegistry;
+import android.net.Network;
+
+/**
+ * A pair of Network and NetworkAgentRegistry.
+ *
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable NetworkAndAgentRegistryParcelable {
+ Network network;
+ INetworkAgentRegistry registry;
+}
diff --git a/networksecurity/TEST_MAPPING b/networksecurity/TEST_MAPPING
index 448ee84..f75bf9a 100644
--- a/networksecurity/TEST_MAPPING
+++ b/networksecurity/TEST_MAPPING
@@ -1,4 +1,9 @@
{
+ "tethering-mainline-presubmit": [
+ {
+ "name": "NetworkSecurityUnitTests"
+ }
+ ],
"presubmit": [
{
"name": "CtsNetSecConfigCertificateTransparencyTestCases"
diff --git a/networksecurity/tests/unit/Android.bp b/networksecurity/tests/unit/Android.bp
index 11263cf..1336acc 100644
--- a/networksecurity/tests/unit/Android.bp
+++ b/networksecurity/tests/unit/Android.bp
@@ -41,4 +41,5 @@
],
sdk_version: "test_current",
+ min_sdk_version: "VanillaIceCream",
}
diff --git a/networksecurity/tests/unit/AndroidTest.xml b/networksecurity/tests/unit/AndroidTest.xml
index 3c94df7..fc0943e 100644
--- a/networksecurity/tests/unit/AndroidTest.xml
+++ b/networksecurity/tests/unit/AndroidTest.xml
@@ -17,6 +17,12 @@
<configuration description="Runs NetworkSecurity Mainline unit Tests.">
<option name="test-tag" value="NetworkSecurityUnitTests" />
+ <!--
+ Only run tests if the device under test is SDK version 35 (Android 15) or above.
+ -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.Sdk35ModuleController" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="test-file-name" value="NetworkSecurityUnitTests.apk" />
</target_preparer>
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index c0082bb..622fba8 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -104,7 +104,7 @@
// First verify the clatd directory and binary,
// since this is built into the apex file system image,
// failures here are 99% likely to be build problems.
- V(kClatdDir, S_IFDIR|0750, ROOT, SYSTEM, "system_file", DIR);
+ V(kClatdDir, S_IFDIR|0750, CLAT, SYSTEM, "system_file", DIR);
V(kClatdBin, S_IFREG|S_ISUID|S_ISGID|0755, CLAT, CLAT, "clatd_exec", BIN);
// Move on to verifying that the bpf programs and maps are as expected.
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 3ce3f02..329b338 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -228,6 +228,7 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
+import android.net.NetworkAndAgentRegistryParcelable;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -526,6 +527,7 @@
private final boolean mBackgroundFirewallChainEnabled;
private final boolean mUseDeclaredMethodsForCallbacksEnabled;
+ private final boolean mQueueNetworkAgentEventsInSystemServer;
// Flag to delay callbacks for frozen apps, suppressing duplicate and stale callbacks.
private final boolean mQueueCallbacksForFrozenApps;
@@ -1928,6 +1930,9 @@
mUseDeclaredMethodsForCallbacksEnabled =
mDeps.isFeatureNotChickenedOut(context,
ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS);
+ mQueueNetworkAgentEventsInSystemServer =
+ mDeps.isFeatureNotChickenedOut(context,
+ ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER);
// registerUidFrozenStateChangedCallback is only available on U+
mQueueCallbacksForFrozenApps = mDeps.isAtLeastU()
&& mDeps.isFeatureNotChickenedOut(context, QUEUE_CALLBACKS_FOR_FROZEN_APPS);
@@ -4688,18 +4693,30 @@
private void maybeHandleNetworkAgentMessage(Message msg) {
final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
final NetworkAgentInfo nai = arg.first;
- if (!mNetworkAgentInfos.contains(nai)) {
- if (VDBG) {
- log(String.format("%s from unknown NetworkAgent", eventName(msg.what)));
- }
- return;
- }
// If the network has been destroyed, the only thing that it can do is disconnect.
if (nai.isDestroyed() && !isDisconnectRequest(msg)) {
return;
}
+ if (mQueueNetworkAgentEventsInSystemServer && nai.maybeEnqueueMessage(msg)) {
+ // If the message is enqueued, the NAI will replay it immediately
+ // when registration is complete. It does this by sending all the
+ // messages in the order received immediately after the
+ // EVENT_AGENT_REGISTERED message.
+ return;
+ }
+
+ // If the nai has been registered (and doesn't enqueue), it should now be
+ // in the list of NAIs.
+ if (!mNetworkAgentInfos.contains(nai)) {
+ // TODO : this is supposed to be impossible
+ if (VDBG) {
+ log(String.format("%s from unknown NetworkAgent", eventName(msg.what)));
+ }
+ return;
+ }
+
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
final NetworkCapabilities proposed = (NetworkCapabilities) arg.second;
@@ -7494,7 +7511,7 @@
private boolean isLegacyLockdownNai(NetworkAgentInfo nai) {
return mLockdownEnabled
- && getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
+ && isLegacyVpn(nai)
&& nai.networkCapabilities.appliesToUid(Process.FIRST_APPLICATION_UID);
}
@@ -9328,7 +9345,7 @@
* @param providerId the ID of the provider owning this NetworkAgent.
* @return the network created for this agent.
*/
- public Network registerNetworkAgent(INetworkAgent na,
+ public NetworkAndAgentRegistryParcelable registerNetworkAgent(INetworkAgent na,
NetworkInfo networkInfo,
LinkProperties linkProperties,
NetworkCapabilities networkCapabilities,
@@ -9371,7 +9388,8 @@
}
}
- private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
+ private NetworkAndAgentRegistryParcelable registerNetworkAgentInternal(
+ INetworkAgent na, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
NetworkScore currentScore, NetworkAgentConfig networkAgentConfig,
@Nullable LocalNetworkConfig localNetworkConfig, int providerId,
@@ -9403,8 +9421,11 @@
// NetworkAgentInfo registration will finish when the NetworkMonitor is created.
// If the network disconnects or sends any other event before that, messages are deferred by
// NetworkAgent until nai.connect(), which will be called when finalizing the
- // registration.
- return nai.network;
+ // registration. TODO : have NetworkAgentInfo defer them instead.
+ final NetworkAndAgentRegistryParcelable result = new NetworkAndAgentRegistryParcelable();
+ result.network = nai.network;
+ result.registry = nai.getRegistry();
+ return result;
}
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
@@ -9416,8 +9437,6 @@
nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator)));
processLinkPropertiesFromAgent(nai, nai.linkProperties);
- nai.onNetworkMonitorCreated(networkMonitor);
-
mNetworkAgentInfos.add(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(nai.network.getNetId(), nai);
@@ -9432,7 +9451,7 @@
if (nai.isLocalNetwork()) {
handleUpdateLocalNetworkConfig(nai, null /* oldConfig */, nai.localNetworkConfig);
}
- nai.notifyRegistered();
+ nai.notifyRegistered(networkMonitor);
NetworkInfo networkInfo = nai.networkInfo;
updateNetworkInfo(nai, networkInfo);
updateVpnUids(nai, null, nai.networkCapabilities);
@@ -10133,8 +10152,8 @@
* interfaces.
* Ingress discard rule is added to the address iff
* 1. The address is not a link local address
- * 2. The address is used by a single interface of VPN whose VPN type is not TYPE_VPN_LEGACY
- * or TYPE_VPN_OEM and the address is not used by any other interfaces even non-VPN ones
+ * 2. The address is used by a single interface of VPN whose VPN type is not LEGACY, OEM or
+ * OEM_LEGACY and the address is not used by any other interfaces even non-VPN ones
* Ingress discard rule is not be added to TYPE_VPN_LEGACY or TYPE_VPN_OEM VPN since these VPNs
* might need to receive packet to VPN address via non-VPN interface.
* This method can be called during network disconnects, when nai has already been removed from
@@ -10172,9 +10191,7 @@
final Set<Pair<InetAddress, String>> ingressDiscardRules = new ArraySet<>();
for (final NetworkAgentInfo agent : nais) {
final int vpnType = getVpnType(agent);
- if (!agent.isVPN() || agent.isDestroyed()
- || vpnType == VpnManager.TYPE_VPN_LEGACY
- || vpnType == VpnManager.TYPE_VPN_OEM) {
+ if (!agent.isVPN() || agent.isDestroyed() || !vpnSupportsInterfaceFiltering(agent)) {
continue;
}
final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
@@ -12817,6 +12834,23 @@
return ((VpnTransportInfo) ti).getType();
}
+ private boolean isVpnServiceVpn(NetworkAgentInfo nai) {
+ final int vpnType = getVpnType(nai);
+ return vpnType == VpnManager.TYPE_VPN_SERVICE || vpnType == VpnManager.TYPE_VPN_OEM_SERVICE;
+ }
+
+ private boolean isLegacyVpn(NetworkAgentInfo nai) {
+ final int vpnType = getVpnType(nai);
+ return vpnType == VpnManager.TYPE_VPN_LEGACY || vpnType == VpnManager.TYPE_VPN_OEM_LEGACY;
+ }
+
+ private boolean vpnSupportsInterfaceFiltering(NetworkAgentInfo vpn) {
+ final int vpnType = getVpnType(vpn);
+ return vpnType != VpnManager.TYPE_VPN_LEGACY
+ && vpnType != VpnManager.TYPE_VPN_OEM
+ && vpnType != VpnManager.TYPE_VPN_OEM_LEGACY;
+ }
+
private void maybeUpdateWifiRoamTimestamp(@NonNull NetworkAgentInfo nai,
@NonNull NetworkCapabilities nc) {
final TransportInfo prevInfo = nai.networkCapabilities.getTransportInfo();
@@ -12850,7 +12884,7 @@
if (hasNetworkStackPermission()) return uid;
final NetworkAgentInfo vpn = getVpnForUid(uid);
- if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE
+ if (vpn == null || !isVpnServiceVpn(vpn)
|| vpn.networkCapabilities.getOwnerUid() != mDeps.getCallingUid()) {
return INVALID_UID;
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 136ea81..74bd235 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -62,6 +62,9 @@
public static final String QUEUE_CALLBACKS_FOR_FROZEN_APPS =
"queue_callbacks_for_frozen_apps";
+ public static final String QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER =
+ "queue_network_agent_events_in_system_server";
+
private boolean mNoRematchAllRequestsOnRegister;
/**
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index e762a8e..4540f02 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -62,6 +62,7 @@
import android.net.TcpKeepalivePacketData;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
@@ -630,6 +631,7 @@
// Used by ConnectivityService to keep track of 464xlat.
public final Nat464Xlat clatd;
+ private final ArrayList<Message> mMessagesPendingRegistration = new ArrayList<>();
// Set after asynchronous creation of the NetworkMonitor.
private volatile NetworkMonitorManager mNetworkMonitor;
@@ -639,6 +641,7 @@
private final ConnectivityService.Dependencies mConnServiceDeps;
private final Context mContext;
private final Handler mHandler;
+ private final NetworkAgentMessageHandler mRegistry;
private final QosCallbackTracker mQosCallbackTracker;
private final INetd mNetd;
@@ -673,6 +676,7 @@
mNetd = netd;
mContext = context;
mHandler = handler;
+ mRegistry = new NetworkAgentMessageHandler(mHandler);
this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
@@ -698,10 +702,12 @@
* Must be called from the ConnectivityService handler thread. A NetworkAgent can only be
* registered once.
*/
- public void notifyRegistered() {
+ public void notifyRegistered(final INetworkMonitor nm) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ mNetworkMonitor = new NetworkMonitorManager(nm);
try {
networkAgent.asBinder().linkToDeath(mDeathMonitor, 0);
- networkAgent.onRegistered(new NetworkAgentMessageHandler(mHandler));
+ networkAgent.onRegistered();
} catch (RemoteException e) {
Log.e(TAG, "Error registering NetworkAgent", e);
maybeUnlinkDeathMonitor();
@@ -711,6 +717,29 @@
}
mHandler.obtainMessage(EVENT_AGENT_REGISTERED, ARG_AGENT_SUCCESS, 0, this).sendToTarget();
+ for (final Message enqueued : mMessagesPendingRegistration) {
+ mHandler.sendMessage(enqueued);
+ }
+ mMessagesPendingRegistration.clear();
+ }
+
+ /**
+ * Enqueues a message if it needs to be enqueued, and returns whether it was enqueued.
+ *
+ * The message is enqueued iff it can't be sent just yet. If it can be sent
+ * immediately, this method returns false and doesn't enqueue.
+ *
+ * If it enqueues, this method will make a copy of the message for enqueuing since
+ * messages can't be reused or recycled before the end of their processing by the
+ * handler.
+ */
+ public boolean maybeEnqueueMessage(final Message msg) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ if (null != mNetworkMonitor) return false;
+ final Message m = mHandler.obtainMessage();
+ m.copyFrom(msg);
+ mMessagesPendingRegistration.add(m);
+ return true;
}
/**
@@ -1036,13 +1065,6 @@
}
/**
- * Inform NetworkAgentInfo that a new NetworkMonitor was created.
- */
- public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
- mNetworkMonitor = new NetworkMonitorManager(networkMonitor);
- }
-
- /**
* Set the NetworkCapabilities on this NetworkAgentInfo. Also attempts to notify NetworkMonitor
* of the new capabilities, if NetworkMonitor has been created.
*
@@ -1117,6 +1139,13 @@
return mNetworkMonitor;
}
+ /**
+ * Get the registry in this NetworkAgentInfo.
+ */
+ public INetworkAgentRegistry getRegistry() {
+ return mRegistry;
+ }
+
// Functions for manipulating the requests satisfied by this network.
//
// These functions must only called on ConnectivityService's main thread.
diff --git a/staticlibs/netd/Android.bp b/staticlibs/netd/Android.bp
index 03f5f06..9222b17 100644
--- a/staticlibs/netd/Android.bp
+++ b/staticlibs/netd/Android.bp
@@ -22,7 +22,7 @@
sdk_version: "system_current",
min_sdk_version: "30",
static_libs: [
- "netd_aidl_interface-V16-java",
+ "netd_aidl_interface-V17-java",
],
apex_available: [
"//apex_available:platform", // used from services.net
@@ -45,7 +45,7 @@
cc_library_static {
name: "netd_aidl_interface-lateststable-ndk",
whole_static_libs: [
- "netd_aidl_interface-V16-ndk",
+ "netd_aidl_interface-V17-ndk",
],
apex_available: [
"com.android.resolv",
@@ -56,12 +56,12 @@
cc_defaults {
name: "netd_aidl_interface_lateststable_cpp_static",
- static_libs: ["netd_aidl_interface-V16-cpp"],
+ static_libs: ["netd_aidl_interface-V17-cpp"],
}
cc_defaults {
name: "netd_aidl_interface_lateststable_cpp_shared",
- shared_libs: ["netd_aidl_interface-V16-cpp"],
+ shared_libs: ["netd_aidl_interface-V17-cpp"],
}
aidl_interface {
@@ -175,6 +175,10 @@
version: "16",
imports: [],
},
+ {
+ version: "17",
+ imports: [],
+ },
],
frozen: true,
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash b/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash
new file mode 100644
index 0000000..f69c88b
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash
@@ -0,0 +1 @@
+a786da2fe41bda627a8c1e63b99264a415e769c8
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl
new file mode 100644
index 0000000..8351b56
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl
@@ -0,0 +1,272 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetd {
+ boolean isAlive();
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids);
+ boolean bandwidthEnableDataSaver(boolean enable);
+ /**
+ * @deprecated use networkCreate() instead.
+ */
+ void networkCreatePhysical(int netId, int permission);
+ /**
+ * @deprecated use networkCreate() instead.
+ */
+ void networkCreateVpn(int netId, boolean secure);
+ void networkDestroy(int netId);
+ void networkAddInterface(int netId, in @utf8InCpp String iface);
+ void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+ void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+ void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+ boolean tetherApplyDnsInterfaces();
+ android.net.TetherStatsParcel[] tetherGetStats();
+ void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+ void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+ void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+ int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+ void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+ void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+ void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+ void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+ void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void strictUidCleartextPenalty(int uid, int policyPenalty);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The clatd control plane moved to the mainline module starting in T. See ClatCoordinator.
+ */
+ @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The clatd control plane moved to the mainline module starting in T. See ClatCoordinator.
+ */
+ void clatdStop(in @utf8InCpp String ifName);
+ boolean ipfwdEnabled();
+ @utf8InCpp String[] ipfwdGetRequesterList();
+ void ipfwdEnableForwarding(in @utf8InCpp String requester);
+ void ipfwdDisableForwarding(in @utf8InCpp String requester);
+ void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+ void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+ void bandwidthSetGlobalAlert(long bytes);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void bandwidthAddNaughtyApp(int uid);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void bandwidthRemoveNaughtyApp(int uid);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void bandwidthAddNiceApp(int uid);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void bandwidthRemoveNiceApp(int uid);
+ void tetherStart(in @utf8InCpp String[] dhcpRanges);
+ void tetherStop();
+ boolean tetherIsEnabled();
+ void tetherInterfaceAdd(in @utf8InCpp String ifName);
+ void tetherInterfaceRemove(in @utf8InCpp String ifName);
+ @utf8InCpp String[] tetherInterfaceList();
+ void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+ @utf8InCpp String[] tetherDnsList();
+ void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ int networkGetDefault();
+ void networkSetDefault(int netId);
+ void networkClearDefault();
+ void networkSetPermissionForNetwork(int netId, int permission);
+ void networkSetPermissionForUser(int permission, in int[] uids);
+ void networkClearPermissionForUser(in int[] uids);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void trafficSetNetPermForUids(int permission, in int[] uids);
+ void networkSetProtectAllow(int uid);
+ void networkSetProtectDeny(int uid);
+ boolean networkCanProtect(int uid);
+ void firewallSetFirewallType(int firewalltype);
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void firewallEnableChildChain(int childChain, boolean enable);
+ @utf8InCpp String[] interfaceGetList();
+ android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+ void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+ void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+ void interfaceClearAddrs(in @utf8InCpp String ifName);
+ void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+ void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+ void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+ void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void firewallRemoveUidInterfaceRules(in int[] uids);
+ /**
+ * @deprecated unimplemented on T+.
+ */
+ void trafficSwapActiveStatsMap();
+ IBinder getOemNetd();
+ void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+ android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+ */
+ void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+ */
+ void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+ */
+ android.net.TetherStatsParcel[] tetherOffloadGetStats();
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+ */
+ void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+ /**
+ * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+ */
+ android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
+ void networkCreate(in android.net.NativeNetworkConfig config);
+ void networkAddUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+ void networkRemoveUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+ void ipSecMigrate(in android.net.IpSecMigrateInfoParcel migrateInfo);
+ void setNetworkAllowlist(in android.net.netd.aidl.NativeUidRangeConfig[] allowedNetworks);
+ void networkAllowBypassVpnOnNetwork(boolean allow, int uid, int netId);
+ const int IPV4 = 4;
+ const int IPV6 = 6;
+ const int CONF = 1;
+ const int NEIGH = 2;
+ const String IPSEC_INTERFACE_PREFIX = "ipsec";
+ const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+ const int IPV6_ADDR_GEN_MODE_NONE = 1;
+ const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+ const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+ const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+ const int PENALTY_POLICY_ACCEPT = 1;
+ const int PENALTY_POLICY_LOG = 2;
+ const int PENALTY_POLICY_REJECT = 3;
+ const int CLAT_MARK = 0xdeadc1a7;
+ const int LOCAL_NET_ID = 99;
+ const int DUMMY_NET_ID = 51;
+ const int UNREACHABLE_NET_ID = 52;
+ const String NEXTHOP_NONE = "";
+ const String NEXTHOP_UNREACHABLE = "unreachable";
+ const String NEXTHOP_THROW = "throw";
+ const int PERMISSION_NONE = 0;
+ const int PERMISSION_NETWORK = 1;
+ const int PERMISSION_SYSTEM = 2;
+ /**
+ * @deprecated usage is internal to module.
+ */
+ const int NO_PERMISSIONS = 0;
+ /**
+ * @deprecated usage is internal to module.
+ */
+ const int PERMISSION_INTERNET = 4;
+ /**
+ * @deprecated usage is internal to module.
+ */
+ const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+ /**
+ * @deprecated usage is internal to module.
+ */
+ const int PERMISSION_UNINSTALLED = (-1) /* -1 */;
+ /**
+ * @deprecated use FIREWALL_ALLOWLIST.
+ */
+ const int FIREWALL_WHITELIST = 0;
+ const int FIREWALL_ALLOWLIST = 0;
+ /**
+ * @deprecated use FIREWALL_DENYLIST.
+ */
+ const int FIREWALL_BLACKLIST = 1;
+ const int FIREWALL_DENYLIST = 1;
+ const int FIREWALL_RULE_ALLOW = 1;
+ const int FIREWALL_RULE_DENY = 2;
+ const int FIREWALL_CHAIN_NONE = 0;
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+ const int FIREWALL_CHAIN_RESTRICTED = 4;
+ const String IF_STATE_UP = "up";
+ const String IF_STATE_DOWN = "down";
+ const String IF_FLAG_BROADCAST = "broadcast";
+ const String IF_FLAG_LOOPBACK = "loopback";
+ const String IF_FLAG_POINTOPOINT = "point-to-point";
+ const String IF_FLAG_RUNNING = "running";
+ const String IF_FLAG_MULTICAST = "multicast";
+ const int IPSEC_DIRECTION_IN = 0;
+ const int IPSEC_DIRECTION_OUT = 1;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..31775df
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+ oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+ oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+ oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+ oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAdded(@utf8InCpp String ifName);
+ oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+ oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+ oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..1869d8d
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+ @utf8InCpp String ifName;
+ @utf8InCpp String hwAddr;
+ @utf8InCpp String ipv4Addr;
+ int prefixLength;
+ @utf8InCpp String[] flags;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl
new file mode 100644
index 0000000..975a261
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@JavaOnlyImmutable
+parcelable IpSecMigrateInfoParcel {
+ int requestId;
+ int selAddrFamily;
+ int direction;
+ @utf8InCpp String oldSourceAddress;
+ @utf8InCpp String oldDestinationAddress;
+ @utf8InCpp String newSourceAddress;
+ @utf8InCpp String newDestinationAddress;
+ int interfaceId;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..8ea20d1
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable MarkMaskParcel {
+ int mark;
+ int mask;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl
new file mode 100644
index 0000000..77d814b
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeNetworkConfig {
+ int netId;
+ android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL;
+ int permission;
+ boolean secure;
+ android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM;
+ boolean excludeLocalRoutes = false;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl
new file mode 100644
index 0000000..e77a143
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeNetworkType {
+ PHYSICAL = 0,
+ VIRTUAL = 1,
+ PHYSICAL_LOCAL = 2,
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl
new file mode 100644
index 0000000..a6af5f7
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeVpnType {
+ SERVICE = 1,
+ PLATFORM = 2,
+ LEGACY = 3,
+ OEM = 4,
+ OEM_SERVICE = 5,
+ OEM_LEGACY = 6,
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5ef95e6
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable RouteInfoParcel {
+ @utf8InCpp String destination;
+ @utf8InCpp String ifName;
+ @utf8InCpp String nextHop;
+ int mtu;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..7b39c22
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherConfigParcel {
+ boolean usingLegacyDnsProxy;
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..983e986
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+ int inputInterfaceIndex;
+ int outputInterfaceIndex;
+ byte[] destination;
+ int prefixLength;
+ byte[] srcL2Address;
+ byte[] dstL2Address;
+ int pmtu = 1500;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..5f1b722
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherStatsParcel {
+ @utf8InCpp String iface;
+ long rxBytes;
+ long rxPackets;
+ long txBytes;
+ long txPackets;
+ int ifIndex = 0;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..72e987a
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable UidRangeParcel {
+ int start;
+ int stop;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl
new file mode 100644
index 0000000..9bb679f
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.netd.aidl;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeUidRangeConfig {
+ int netId;
+ android.net.UidRangeParcel[] uidRanges;
+ int subPriority;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
index 8a8be83..a6af5f7 100644
--- a/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
@@ -38,4 +38,6 @@
PLATFORM = 2,
LEGACY = 3,
OEM = 4,
+ OEM_SERVICE = 5,
+ OEM_LEGACY = 6,
}
diff --git a/staticlibs/netd/binder/android/net/NativeVpnType.aidl b/staticlibs/netd/binder/android/net/NativeVpnType.aidl
index cd1b447..aa0fdc1 100644
--- a/staticlibs/netd/binder/android/net/NativeVpnType.aidl
+++ b/staticlibs/netd/binder/android/net/NativeVpnType.aidl
@@ -34,7 +34,20 @@
LEGACY = 3,
/**
- * An VPN created by OEM code through other means than VpnService or VpnManager.
+ * A VPN created by OEM code through other means than VpnService or VpnManager.
*/
OEM = 4,
-}
\ No newline at end of file
+
+ /**
+ * A VPN created by OEM code using VpnService, and which OEM code desires to differentiate from
+ * other VPN types. The core networking stack will treat this VPN type similarly to SERVICE.
+ */
+ OEM_SERVICE = 5,
+
+ /**
+ * A VPN created by OEM code using the legacy VPN mechanisms, and which OEM code desires to
+ * differentiate from other VPN types. The core networking stack will treat this VPN type
+ * similarly to LEGACY.
+ */
+ OEM_LEGACY = 6,
+}
diff --git a/staticlibs/tests/unit/host/python/apf_utils_test.py b/staticlibs/tests/unit/host/python/apf_utils_test.py
index 348df3b..d4753b7 100644
--- a/staticlibs/tests/unit/host/python/apf_utils_test.py
+++ b/staticlibs/tests/unit/host/python/apf_utils_test.py
@@ -26,7 +26,7 @@
get_apf_counter,
get_apf_counters_from_dumpsys,
get_ipv4_addresses,
- get_ipv6_addresses,
+ get_non_tentative_ipv6_addresses,
get_hardware_address,
is_send_raw_packet_downstream_supported,
is_packet_capture_supported,
@@ -144,7 +144,7 @@
asserts.assert_equal(ip_addresses, [])
@patch("net_tests_utils.host.python.adb_utils.adb_shell")
- def test_get_ipv6_addresses_success(
+ def test_get_non_tentative_ipv6_addresses_success(
self, mock_adb_shell: MagicMock
) -> None:
mock_adb_shell.return_value = """
@@ -156,7 +156,7 @@
inet6 fe80::3aff:2199:2d8e:20d1/64 scope link noprefixroute
valid_lft forever preferred_lft forever
"""
- ip_addresses = get_ipv6_addresses(self.mock_ad, "wlan0")
+ ip_addresses = get_non_tentative_ipv6_addresses(self.mock_ad, "wlan0")
asserts.assert_equal(ip_addresses,
["fe80::10a3:5dff:fe52:de32",
"2001:b400:e53f:164e:9c1e:780e:d1:4658",
@@ -167,7 +167,7 @@
self, mock_adb_shell: MagicMock
) -> None:
mock_adb_shell.return_value = ""
- ip_addresses = get_ipv6_addresses(self.mock_ad, "wlan0")
+ ip_addresses = get_non_tentative_ipv6_addresses(self.mock_ad, "wlan0")
asserts.assert_equal(ip_addresses, [])
@patch("net_tests_utils.host.python.adb_utils.adb_shell")
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
index ae0de79..c9d2527 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
@@ -18,20 +18,28 @@
import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.READ_PHONE_STATE
+import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.os.ConditionVariable
+import android.os.ParcelFileDescriptor
import android.os.PersistableBundle
+import android.os.Process
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
+import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
+import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.modules.utils.build.SdkLevel.isAtLeastU
+import com.android.modules.utils.build.SdkLevel
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
+import java.security.MessageDigest
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import org.junit.rules.TestRule
@@ -46,12 +54,20 @@
* configuration automatically on teardown.
*/
class CarrierConfigRule : TestRule {
+ private val HEX_CHARS: CharArray = charArrayOf(
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ )
+
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val uiAutomation by lazy { InstrumentationRegistry.getInstrumentation().uiAutomation }
private val ccm by lazy { context.getSystemService(CarrierConfigManager::class.java) }
// Map of (subId) -> (original values of overridden settings)
private val originalConfigs = mutableMapOf<Int, PersistableBundle>()
+ // Map of (subId) -> (original values of carrier service package)
+ private val originalCarrierServicePackages = mutableMapOf<Int, String?>()
+
override fun apply(base: Statement, description: Description): Statement {
return CarrierConfigStatement(base, description)
}
@@ -118,6 +134,177 @@
}
}
+ private fun runShellCommand(cmd: String) {
+ val fd: ParcelFileDescriptor = uiAutomation.executeShellCommand(cmd)
+ fd.close() // Don't care about the output.
+ }
+
+ /**
+ * Converts a byte array into a String of hexadecimal characters.
+ *
+ * @param bytes an array of bytes
+ * @return hex string representation of bytes array
+ */
+ private fun bytesToHexString(bytes: ByteArray?): String? {
+ if (bytes == null) return null
+
+ val ret = StringBuilder(2 * bytes.size)
+
+ for (i in bytes.indices) {
+ var b: Int
+ b = 0x0f and (bytes[i].toInt() shr 4)
+ ret.append(HEX_CHARS[b])
+ b = 0x0f and bytes[i].toInt()
+ ret.append(HEX_CHARS[b])
+ }
+
+ return ret.toString()
+ }
+
+ private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
+ if (!SdkLevel.isAtLeastT()) {
+ throw UnsupportedOperationException(
+ "Acquiring carrier privilege requires at least T SDK"
+ )
+ }
+
+ fun getCertHash(): String {
+ val pkgInfo = context.packageManager.getPackageInfo(
+ context.opPackageName,
+ PackageManager.GET_SIGNATURES
+ )
+ val digest = MessageDigest.getInstance("SHA-256")
+ val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
+ return bytesToHexString(certHash)!!
+ }
+
+ val tm = context.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = PrivilegeWaiterCallback(cv)
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't register CarrierPrivilegesCallback")
+ if (cpb.hasPrivilege == hold) {
+ if (hold) {
+ Log.w(TAG, "Package ${context.opPackageName} already is privileged")
+ } else {
+ Log.w(TAG, "Package ${context.opPackageName} already isn't privileged")
+ }
+ return@tryTest
+ }
+ if (hold) {
+ addConfigOverrides(subId, PersistableBundle().also {
+ it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ arrayOf(getCertHash()))
+ })
+ } else {
+ cleanUpNow()
+ }
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ /**
+ * Acquires carrier privilege on the given subscription ID.
+ */
+ fun acquireCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(true, subId)
+
+ /**
+ * Drops carrier privilege from the given subscription ID.
+ */
+ fun dropCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(false, subId)
+
+ /**
+ * Sets the carrier service package override for the given subscription ID. A null argument will
+ * clear any previously-set override.
+ */
+ fun setCarrierServicePackageOverride(subId: Int, pkg: String?) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw UnsupportedOperationException(
+ "Setting carrier service package override requires at least U SDK"
+ )
+ }
+
+ val tm = context.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = CarrierServiceChangedWaiterCallback(cv)
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't register CarrierPrivilegesCallback")
+ if (cpb.pkgName == pkg) {
+ Log.w(TAG, "Carrier service package was already $pkg")
+ return@tryTest
+ }
+ if (!originalCarrierServicePackages.contains(subId)) {
+ originalCarrierServicePackages.put(subId, cpb.pkgName)
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ if (null == pkg) {
+ // There is a bug in clear-carrier-service-package-override where not adding
+ // the -s argument will use the wrong slot index : b/299604822
+ runShellCommand("cmd phone clear-carrier-service-package-override" +
+ " -s $subId")
+ } else {
+ runShellCommand("cmd phone set-carrier-service-package-override $pkg" +
+ " -s $subId")
+ }
+ }
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't modify carrier service package")
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private class PrivilegeWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var hasPrivilege = false
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, uids: MutableSet<Int>) {
+ hasPrivilege = uids.contains(Process.myUid())
+ cv.open()
+ }
+ }
+
+ private class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var pkgName: String? = null
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, u: MutableSet<Int>) {}
+ override fun onCarrierServiceChanged(pkgName: String?, uid: Int) {
+ this.pkgName = pkgName
+ cv.open()
+ }
+ }
+
/**
* Cleanup overrides that were added by the test case.
*
@@ -138,6 +325,10 @@
}
originalConfigs.clear()
}
+ originalCarrierServicePackages.forEach { (subId, pkg) ->
+ setCarrierServicePackageOverride(subId, pkg)
+ }
+ originalCarrierServicePackages.clear()
}
}
@@ -145,7 +336,7 @@
subId: Int,
keys: Set<String>
): PersistableBundle {
- return if (isAtLeastU()) {
+ return if (SdkLevel.isAtLeastU()) {
// This method is U+
getConfigForSubId(subId, *keys.toTypedArray())
} else {
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt b/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
index 8a255c6..60285a8 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
@@ -45,7 +45,6 @@
import java.io.ByteArrayOutputStream
import java.io.CharArrayWriter
import java.io.File
-import java.io.FileOutputStream
import java.io.FileReader
import java.io.OutputStream
import java.io.OutputStreamWriter
@@ -280,7 +279,7 @@
}
val outFile = File(collectorDir, filename + FILENAME_SUFFIX)
outputFiles.add(filename)
- FileOutputStream(outFile).use { fos ->
+ getOutputStreamViaShell(outFile).use { fos ->
failureHeader?.let {
fos.write(it.toByteArray())
fos.write("\n".toByteArray())
diff --git a/staticlibs/testutils/host/python/apf_test_base.py b/staticlibs/testutils/host/python/apf_test_base.py
index 2552aa3..33b3838 100644
--- a/staticlibs/testutils/host/python/apf_test_base.py
+++ b/staticlibs/testutils/host/python/apf_test_base.py
@@ -60,10 +60,10 @@
self.client_ipv4_addresses = apf_utils.get_ipv4_addresses(
self.clientDevice, self.client_iface_name
)
- self.server_ipv6_addresses = apf_utils.get_ipv6_addresses(
+ self.server_ipv6_addresses = apf_utils.get_non_tentative_ipv6_addresses(
self.serverDevice, self.server_iface_name
)
- self.client_ipv6_addresses = apf_utils.get_ipv6_addresses(
+ self.client_ipv6_addresses = apf_utils.get_non_tentative_ipv6_addresses(
self.clientDevice, self.client_iface_name
)
diff --git a/staticlibs/testutils/host/python/apf_utils.py b/staticlibs/testutils/host/python/apf_utils.py
index c2ad18e..1648d36 100644
--- a/staticlibs/testutils/host/python/apf_utils.py
+++ b/staticlibs/testutils/host/python/apf_utils.py
@@ -116,12 +116,12 @@
else:
return []
-def get_ipv6_addresses(
+def get_non_tentative_ipv6_addresses(
ad: android_device.AndroidDevice, iface_name: str
) -> list[str]:
- """Retrieves the IPv6 addresses of a given interface on an Android device.
+ """Retrieves the non-tentative IPv6 addresses of a given interface on an Android device.
- This function executes an ADB shell command (`ip -6 address show`) to get the
+ This function executes an ADB shell command (`ip -6 address show -tentative`) to get the
network interface information and extracts the IPv6 address from the output.
If devices have no IPv6 address, raise PatternNotFoundException.
@@ -139,7 +139,7 @@
# valid_lft forever preferred_lft forever
# inet6 fe80::1233:aadb:3d32:1234/64 scope link
# valid_lft forever preferred_lft forever
- output = adb_utils.adb_shell(ad, f"ip -6 address show {iface_name}")
+ output = adb_utils.adb_shell(ad, f"ip -6 address show -tentative {iface_name}")
pattern = r"inet6\s+([0-9a-fA-F:]+)\/\d+"
matches = re.findall(pattern, output)
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index 61f1bfc..b82a3be 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -13,8 +13,9 @@
# limitations under the License.
from mobly import asserts
-from scapy.layers.inet import IP, ICMP
+from scapy.layers.inet import IP, ICMP, IPOption_Router_Alert
from scapy.layers.l2 import Ether
+from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mq, IGMPv3mr, IGMPv3gr
from net_tests_utils.host.python import apf_test_base, apf_utils, adb_utils, assert_utils, packet_utils
APFV6_VERSION = 6000
@@ -98,4 +99,48 @@
expected_echo_reply = bytes(eth/ip/icmp/b"hello").hex()
self.send_packet_and_expect_reply_received(
echo_request, "DROPPED_IPV4_PING_REQUEST_REPLIED", expected_echo_reply
- )
\ No newline at end of file
+ )
+
+ @apf_utils.at_least_B()
+ def test_igmpv3_general_query_offload(self):
+ ether = Ether(src=self.server_mac_address, dst='01:00:5e:00:00:01')
+ ip = IP(
+ src=self.server_ipv4_addresses[0],
+ dst='224.0.0.1',
+ options=[IPOption_Router_Alert()]
+ )
+ igmp = IGMPv3(type=0x11)/IGMPv3mq()
+ igmpv3_general_query = bytes(ether/ip/igmp).hex()
+
+ mcast_addrs = ['239.0.0.1', '239.0.0.2', '239.0.0.3']
+
+ for addr in mcast_addrs:
+ adb_utils.adb_shell(
+ self.clientDevice,
+ f'ip addr add {addr}/32 dev {self.client_iface_name} autojoin'
+ )
+
+ ether = Ether(src=self.client_mac_address, dst='01:00:5e:00:00:16')
+ ip = IP(
+ src=self.client_ipv4_addresses[0],
+ dst='224.0.0.22',
+ options=[IPOption_Router_Alert()],
+ id=0,
+ flags="DF"
+ )
+ igmpv3_hdr = IGMPv3(type=0x22)
+ mcast_records = []
+ for addr in mcast_addrs:
+ mcast_records.append(IGMPv3gr(rtype=2, maddr=addr))
+
+ igmp = IGMPv3mr(records=mcast_records)
+ expected_igmpv3_report = bytes(ether/ip/igmpv3_hdr/igmp).hex()
+ self.send_packet_and_expect_reply_received(
+ igmpv3_general_query, "DROPPED_IGMP_V3_GENERAL_QUERY_REPLIED", expected_igmpv3_report
+ )
+
+ for addr in mcast_addrs:
+ adb_utils.adb_shell(
+ self.clientDevice,
+ f'ip addr del {addr}/32 dev {self.client_iface_name}'
+ )
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 81afabc..7dbb9b2 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -372,7 +372,7 @@
if (caps.apfVersionSupported > 4) {
assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
- assertThat(caps.apfVersionSupported).isEqualTo(6000) // v6.0000
+ assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100) // v6.000 or v6.100
}
// DEVICEs launching with Android 15 (AOSP experimental) or higher with CHIPSETs that set
@@ -383,14 +383,22 @@
assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
}
- // CHIPSETs (or DEVICES with CHIPSETs) that set ro.board.first_api_level or
- // ro.board.api_level to 202504 or higher:
- // - [VSR-5.3.12-018] MUST implement version 6 of the Android Packet Filtering (APF)
- // interpreter in the Wi-Fi firmware.
- // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM.
+ // DEVICEs with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202504
+ // or higher:
+ // - [VSR-5.3.12-018] MUST implement version 6 or version 6.1 of the Android Packet
+ // Filtering (APF) interpreter in the Wi-Fi firmware.
+ // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM when version 6 is
+ // implemented OR 3000 bytes when version 6.1 is implemented.
+ // - Note, the APF RAM requirement for APF version 6.1 will become 4000 bytes in Android 17
+ // with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202604 or
+ // higher.
if (vsrApiLevel >= 202504) {
- assertThat(caps.apfVersionSupported).isEqualTo(6000)
- assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+ assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100)
+ if (caps.apfVersionSupported == 6000) {
+ assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+ } else {
+ assertThat(caps.maximumApfProgramSize).isAtLeast(3000)
+ }
}
// ApfFilter does not support anything but ARPHRD_ETHER.
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 5e035a2..1fa9e3a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -15,7 +15,6 @@
*/
package android.net.cts
-import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.NETWORK_SETTINGS
import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.app.Instrumentation
@@ -80,12 +79,10 @@
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
import android.net.wifi.WifiInfo
import android.os.Build
-import android.os.ConditionVariable
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
-import android.os.PersistableBundle
import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
@@ -94,19 +91,15 @@
import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
-import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
-import android.telephony.TelephonyManager.CarrierPrivilegesCallback
import android.telephony.data.EpsBearerQosSessionAttributes
import android.util.ArraySet
import android.util.DebugUtils.valueToString
-import android.util.Log
import androidx.test.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingSupplier
-import com.android.compatibility.common.util.UiccUtil
import com.android.modules.utils.build.SdkLevel
import com.android.net.module.util.ArrayTrackRecord
import com.android.net.module.util.NetworkStackConstants.ETHER_MTU
@@ -151,13 +144,12 @@
import java.net.InetSocketAddress
import java.net.Socket
import java.nio.ByteBuffer
-import java.security.MessageDigest
import java.time.Duration
import java.util.Arrays
-import java.util.Random
import java.util.UUID
import java.util.concurrent.Executors
import kotlin.collections.ArrayList
+import kotlin.random.Random
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
@@ -267,7 +259,7 @@
private class FakeConnectivityService {
val mockRegistry = mock(INetworkAgentRegistry::class.java)
private var agentField: INetworkAgent? = null
- private val registry = object : INetworkAgentRegistry.Stub(),
+ val registry: INetworkAgentRegistry = object : INetworkAgentRegistry.Stub(),
INetworkAgentRegistry by mockRegistry {
// asBinder has implementations in both INetworkAgentRegistry.Stub and mockRegistry, so
// it needs to be disambiguated. Just fail the test as it should be unused here.
@@ -284,7 +276,7 @@
fun connect(agent: INetworkAgent) {
this.agentField = agent
- agent.onRegistered(registry)
+ agent.onRegistered()
}
fun disconnect() = agent.onDisconnected()
@@ -413,7 +405,8 @@
}
private fun createNetworkAgentWithFakeCS() = createNetworkAgent().also {
- mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
+ val binder = it.registerForTest(Network(FAKE_NET_ID), mFakeConnectivityService.registry)
+ mFakeConnectivityService.connect(binder)
}
private fun TestableNetworkAgent.expectPostConnectionCallbacks(
@@ -708,102 +701,6 @@
doTestAllowedUids(transports, uid, expectUidsPresent, specifier, transportInfo)
}
- private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
- fun getCertHash(): String {
- val pkgInfo = realContext.packageManager.getPackageInfo(
- realContext.opPackageName,
- PackageManager.GET_SIGNATURES
- )
- val digest = MessageDigest.getInstance("SHA-256")
- val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
- return UiccUtil.bytesToHexString(certHash)!!
- }
-
- val tm = realContext.getSystemService(TelephonyManager::class.java)!!
-
- val cv = ConditionVariable()
- val cpb = PrivilegeWaiterCallback(cv)
- // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
- // T. This means the lambda will compile as a private method of this class taking a
- // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
- // including private methods, this would fail with a link error when running on S-.
- // To solve this, make the lambda serializable, which causes the compiler to emit a
- // synthetic class instead of a synthetic method.
- tryTest @JvmSerializableLambda {
- val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
- }
- // Wait for the callback to be registered
- assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
- if (cpb.hasPrivilege == hold) {
- if (hold) {
- Log.w(TAG, "Package ${realContext.opPackageName} already is privileged")
- } else {
- Log.w(TAG, "Package ${realContext.opPackageName} already isn't privileged")
- }
- return@tryTest
- }
- if (hold) {
- carrierConfigRule.addConfigOverrides(subId, PersistableBundle().also {
- it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
- arrayOf(getCertHash()))
- })
- } else {
- carrierConfigRule.cleanUpNow()
- }
- } cleanup @JvmSerializableLambda {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- tm.unregisterCarrierPrivilegesCallback(cpb)
- }
- }
- }
-
- private fun acquireCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(true, subId)
- private fun dropCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(false, subId)
-
- private fun setCarrierServicePackageOverride(subId: Int, pkg: String?) {
- val tm = realContext.getSystemService(TelephonyManager::class.java)!!
-
- val cv = ConditionVariable()
- val cpb = CarrierServiceChangedWaiterCallback(cv)
- // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
- // T. This means the lambda will compile as a private method of this class taking a
- // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
- // including private methods, this would fail with a link error when running on S-.
- // To solve this, make the lambda serializable, which causes the compiler to emit a
- // synthetic class instead of a synthetic method.
- tryTest @JvmSerializableLambda {
- val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
- }
- // Wait for the callback to be registered
- assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
- if (cpb.pkgName == pkg) {
- Log.w(TAG, "Carrier service package was already $pkg")
- return@tryTest
- }
- cv.close()
- runAsShell(MODIFY_PHONE_STATE) {
- if (null == pkg) {
- // There is a bug is clear-carrier-service-package-override where not adding
- // the -s argument will use the wrong slot index : b/299604822
- runShellCommand("cmd phone clear-carrier-service-package-override" +
- " -s $subId")
- } else {
- // -s could set the subId, but this test works with the default subId.
- runShellCommand("cmd phone set-carrier-service-package-override $pkg")
- }
- }
- assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't modify carrier service package")
- } cleanup @JvmSerializableLambda {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- tm.unregisterCarrierPrivilegesCallback(cpb)
- }
- }
- }
-
private fun String.execute() = runShellCommand(this).trim()
@Test
@@ -856,8 +753,8 @@
if (!SdkLevel.isAtLeastU()) return@tryTest
// Acquiring carrier privilege is necessary to override the carrier service package.
val defaultSlotIndex = SubscriptionManager.getSlotIndex(defaultSubId)
- acquireCarrierPrivilege(defaultSubId)
- setCarrierServicePackageOverride(defaultSubId, servicePackage)
+ carrierConfigRule.acquireCarrierPrivilege(defaultSubId)
+ carrierConfigRule.setCarrierServicePackageOverride(defaultSubId, servicePackage)
val actualServicePackage: String? = runAsShell(READ_PRIVILEGED_PHONE_STATE) {
tm.getCarrierServicePackageNameForLogicalSlot(defaultSlotIndex)
}
@@ -896,10 +793,6 @@
expectUidsPresent = false)
doTestAllowedUidsWithSubId(defaultSubId, intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI),
uid, expectUidsPresent = false)
- } cleanupStep {
- if (SdkLevel.isAtLeastU()) setCarrierServicePackageOverride(defaultSubId, null)
- } cleanup {
- if (SdkLevel.isAtLeastU()) dropCarrierPrivilege(defaultSubId)
}
}
@@ -1056,6 +949,47 @@
callback.expect<Lost>(agent.network!!)
}
+ fun doTestOemVpnType(type: Int) {
+ val mySessionId = "MySession12345"
+ val nc = NetworkCapabilities().apply {
+ addTransportType(TRANSPORT_TEST)
+ addTransportType(TRANSPORT_VPN)
+ addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ removeCapability(NET_CAPABILITY_NOT_VPN)
+ setTransportInfo(VpnTransportInfo(type, mySessionId))
+ }
+
+ val agent = createNetworkAgent(initialNc = nc)
+ agent.register()
+ agent.markConnected()
+
+ val request = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_VPN)
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build()
+ val callback = TestableNetworkCallback()
+ registerNetworkCallback(request, callback)
+
+ callback.expectAvailableThenValidatedCallbacks(agent.network!!)
+
+ var vpnNc = mCM.getNetworkCapabilities(agent.network!!)
+ assertNotNull(vpnNc)
+ assertEquals(type, (vpnNc!!.transportInfo as VpnTransportInfo).type)
+
+ agent.unregister()
+ callback.expect<Lost>(agent.network!!)
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun testOemVpnTypes() {
+ // TODO: why is this necessary given the @IgnoreUpTo above?
+ assumeTrue(SdkLevel.isAtLeastB())
+ doTestOemVpnType(VpnManager.TYPE_VPN_OEM_SERVICE)
+ doTestOemVpnType(VpnManager.TYPE_VPN_OEM_LEGACY)
+ }
+
private fun unregister(agent: TestableNetworkAgent) {
agent.unregister()
agent.eventuallyExpect<OnNetworkUnwanted>()
@@ -1628,7 +1562,7 @@
val s = Os.socket(AF_INET6, SOCK_DGRAM, 0)
net.bindSocket(s)
val content = ByteArray(16)
- Random().nextBytes(content)
+ Random.nextBytes(content)
Os.sendto(s, ByteBuffer.wrap(content), 0, REMOTE_ADDRESS, 7 /* port */)
val match = reader.poll(DEFAULT_TIMEOUT_MS) {
val udpStart = IPV6_HEADER_LEN + UDP_HEADER_LEN
@@ -2001,25 +1935,3 @@
doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
}
}
-
-// Subclasses of CarrierPrivilegesCallback can't be inline, or they'll be compiled as
-// inner classes of the test class and will fail resolution on R as the test harness
-// uses reflection to list all methods and classes
-class PrivilegeWaiterCallback(private val cv: ConditionVariable) :
- CarrierPrivilegesCallback {
- var hasPrivilege = false
- override fun onCarrierPrivilegesChanged(p: MutableSet<String>, uids: MutableSet<Int>) {
- hasPrivilege = uids.contains(Process.myUid())
- cv.open()
- }
-}
-
-class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) :
- CarrierPrivilegesCallback {
- var pkgName: String? = null
- override fun onCarrierPrivilegesChanged(p: MutableSet<String>, u: MutableSet<Int>) {}
- override fun onCarrierServiceChanged(pkgName: String?, uid: Int) {
- this.pkgName = pkgName
- cv.open()
- }
-}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index 27cba3a..3b8f5bc 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -119,7 +119,10 @@
cv instanceof CallbackValue.OnTetheringStarted);
}
- public void expectTetheringFailed(final int expected) throws InterruptedException {
+ /**
+ * Verify that starting tethering failed with the specified error code.
+ */
+ public void expectTetheringFailed(final int expected) {
final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true);
assertNotNull("No onTetheringFailed after " + TIMEOUT_MS + " ms", cv);
assertTrue("Expect fail with error code " + expected + ", but received: " + cv,
@@ -559,19 +562,28 @@
}
/**
- * Starts Wi-Fi tethering.
+ * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission.
*/
- public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
- throws InterruptedException {
+ public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback) {
return startWifiTethering(callback, null);
}
/**
- * Starts Wi-Fi tethering with the specified SoftApConfiguration.
+ * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission and the specified
+ * SoftApConfiguration.
*/
public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback,
- final SoftApConfiguration softApConfiguration)
- throws InterruptedException {
+ final SoftApConfiguration softApConfiguration) {
+ return runAsShell(TETHER_PRIVILEGED, () -> startWifiTetheringNoPermissions(
+ callback, softApConfiguration));
+ }
+
+ /**
+ * Starts Wi-Fi tethering without any permission with the specified SoftApConfiguration.
+ */
+ public TetheringInterface startWifiTetheringNoPermissions(
+ final TestTetheringEventCallback callback,
+ final SoftApConfiguration softApConfiguration) {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
@@ -582,19 +594,17 @@
}
final TetheringRequest request = builder.build();
- return runAsShell(TETHER_PRIVILEGED, () -> {
- mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
- startTetheringCallback.verifyTetheringStarted();
+ mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
+ startTetheringCallback.verifyTetheringStarted();
- final TetheringInterface iface =
- callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
+ final TetheringInterface iface =
+ callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
- callback.expectOneOfOffloadStatusChanged(
- TETHER_HARDWARE_OFFLOAD_STARTED,
- TETHER_HARDWARE_OFFLOAD_FAILED);
+ callback.expectOneOfOffloadStatusChanged(
+ TETHER_HARDWARE_OFFLOAD_STARTED,
+ TETHER_HARDWARE_OFFLOAD_FAILED);
- return iface;
- });
+ return iface;
}
private static class StopSoftApCallback implements SoftApCallback {
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 7d6a213..abe628b 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -487,6 +487,9 @@
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
mTM.startTethering(new TetheringRequest.Builder(TETHERING_VIRTUAL).build(),
c -> c.run(), startTetheringCallback);
@@ -508,6 +511,7 @@
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
.setWifiSsid(WifiSsid.fromBytes("This is one config"
@@ -532,6 +536,7 @@
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
.setWifiSsid(WifiSsid.fromBytes("This is one config"
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 9538055..f14b922 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2200,6 +2200,7 @@
case ConnectivityFlags.DELAY_DESTROY_SOCKETS:
case ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS:
case ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS:
+ case ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER:
return true;
default:
throw new UnsupportedOperationException("Unknown flag " + name
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index d7e781e..48333c5 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -169,6 +169,7 @@
it[ConnectivityFlags.DELAY_DESTROY_SOCKETS] = true
it[ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS] = true
it[ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS] = true
+ it[ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER] = true
}
fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
index ff0e2c1..a96d06e 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
@@ -16,6 +16,7 @@
package com.android.server.thread;
+import static android.net.thread.ThreadNetworkException.ERROR_UNSUPPORTED_FEATURE;
import static com.android.server.thread.ThreadPersistentSettings.KEY_COUNTRY_CODE;
import android.annotation.Nullable;
@@ -28,11 +29,13 @@
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
+import android.location.LocationListener;
import android.location.LocationManager;
import android.net.thread.IOperationReceiver;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.ActiveCountryCodeChangedCallback;
import android.os.Build;
+import android.os.Bundle;
import android.sysprop.ThreadNetworkProperties;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -115,6 +118,13 @@
new ArrayMap();
private final ThreadPersistentSettings mPersistentSettings;
+ @Nullable private LocationListener mLocationListener;
+ @Nullable private WifiCountryCodeCallback mWifiCountryCodeCallback;
+ @Nullable private BroadcastReceiver mTelephonyBroadcastReceiver;
+
+ /** Indicates whether the Thread co-processor supports setting the country code. */
+ private boolean mIsCpSettingCountryCodeSupported = true;
+
@Nullable private CountryCodeInfo mCurrentCountryCodeInfo;
@Nullable private CountryCodeInfo mLocationCountryCodeInfo;
@Nullable private CountryCodeInfo mOverrideCountryCodeInfo;
@@ -267,13 +277,40 @@
updateCountryCode(false /* forceUpdate */);
}
+ private synchronized void unregisterAllCountryCodeCallbacks() {
+ unregisterGeocoderCountryCodeCallback();
+ unregisterWifiCountryCodeCallback();
+ unregisterTelephonyCountryCodeCallback();
+ }
+
private synchronized void registerGeocoderCountryCodeCallback() {
if ((mGeocoder != null) && isLocationUseForCountryCodeEnabled()) {
+ mLocationListener =
+ new LocationListener() {
+ public void onLocationChanged(Location location) {
+ setCountryCodeFromGeocodingLocation(location);
+ }
+
+ // not used yet
+ public void onProviderDisabled(String provider) {}
+
+ public void onProviderEnabled(String provider) {}
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+ };
+
mLocationManager.requestLocationUpdates(
LocationManager.PASSIVE_PROVIDER,
TIME_BETWEEN_LOCATION_UPDATES_MS,
DISTANCE_BETWEEN_LOCALTION_UPDATES_METERS,
- location -> setCountryCodeFromGeocodingLocation(location));
+ mLocationListener);
+ }
+ }
+
+ private synchronized void unregisterGeocoderCountryCodeCallback() {
+ if (mLocationListener != null) {
+ mLocationManager.removeUpdates(mLocationListener);
+ mLocationListener = null;
}
}
@@ -313,8 +350,16 @@
private synchronized void registerWifiCountryCodeCallback() {
if (mWifiManager != null) {
+ mWifiCountryCodeCallback = new WifiCountryCodeCallback();
mWifiManager.registerActiveCountryCodeChangedCallback(
- r -> r.run(), new WifiCountryCodeCallback());
+ r -> r.run(), mWifiCountryCodeCallback);
+ }
+ }
+
+ private synchronized void unregisterWifiCountryCodeCallback() {
+ if ((mWifiManager != null) && (mWifiCountryCodeCallback != null)) {
+ mWifiManager.unregisterActiveCountryCodeChangedCallback(mWifiCountryCodeCallback);
+ mWifiCountryCodeCallback = null;
}
}
@@ -353,7 +398,7 @@
return;
}
- BroadcastReceiver broadcastReceiver =
+ mTelephonyBroadcastReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -377,11 +422,18 @@
};
mContext.registerReceiver(
- broadcastReceiver,
+ mTelephonyBroadcastReceiver,
new IntentFilter(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED),
Context.RECEIVER_EXPORTED);
}
+ private synchronized void unregisterTelephonyCountryCodeCallback() {
+ if (mTelephonyBroadcastReceiver != null) {
+ mContext.unregisterReceiver(mTelephonyBroadcastReceiver);
+ mTelephonyBroadcastReceiver = null;
+ }
+ }
+
private synchronized void updateTelephonyCountryCodeFromSimCard() {
List<SubscriptionInfo> subscriptionInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
@@ -520,6 +572,11 @@
@Override
public void onError(int otError, String message) {
+ if (otError == ERROR_UNSUPPORTED_FEATURE) {
+ mIsCpSettingCountryCodeSupported = false;
+ unregisterAllCountryCodeCallbacks();
+ }
+
LOG.e(
"Error "
+ otError
@@ -546,6 +603,11 @@
return;
}
+ if (!mIsCpSettingCountryCodeSupported) {
+ LOG.i("Thread co-processor doesn't support setting the country code");
+ return;
+ }
+
LOG.i("Set country code: " + countryCodeInfo);
mThreadNetworkControllerService.setCountryCode(
countryCodeInfo.getCountryCode().toUpperCase(Locale.ROOT),
@@ -592,6 +654,7 @@
/** Dumps the current state of this ThreadNetworkCountryCode object. */
public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("---- Dump of ThreadNetworkCountryCode begin ----");
+ pw.println("mIsCpSettingCountryCodeSupported: " + mIsCpSettingCountryCodeSupported);
pw.println("mOverrideCountryCodeInfo : " + mOverrideCountryCodeInfo);
pw.println("mTelephonyCountryCodeSlotInfoMap: " + mTelephonyCountryCodeSlotInfoMap);
pw.println("mTelephonyCountryCodeInfo : " + mTelephonyCountryCodeInfo);
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
index 139f4c8..6eb9b50 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
@@ -17,6 +17,7 @@
package com.android.server.thread;
import static android.net.thread.ThreadNetworkException.ERROR_INTERNAL_ERROR;
+import static android.net.thread.ThreadNetworkException.ERROR_UNSUPPORTED_FEATURE;
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
import static com.android.server.thread.ThreadPersistentSettings.KEY_COUNTRY_CODE;
@@ -109,6 +110,7 @@
private ThreadNetworkCountryCode mThreadNetworkCountryCode;
private boolean mErrorSetCountryCode;
+ private boolean mErrorUnsupportedFeatureSetCountryCode;
@Captor private ArgumentCaptor<LocationListener> mLocationListenerCaptor;
@Captor private ArgumentCaptor<Geocoder.GeocodeListener> mGeocodeListenerCaptor;
@@ -143,6 +145,10 @@
if (mErrorSetCountryCode) {
cb.onError(ERROR_INTERNAL_ERROR, new String("Invalid country code"));
+ } else if (mErrorUnsupportedFeatureSetCountryCode) {
+ cb.onError(
+ ERROR_UNSUPPORTED_FEATURE,
+ new String("Setting country code is not supported"));
} else {
cb.onSuccess();
}
@@ -453,6 +459,39 @@
}
@Test
+ public void setCountryCodeNotSupported_returnUnsupportedFeatureError_countryCodeNotSetAgain() {
+ mThreadNetworkCountryCode.initialize();
+ assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(DEFAULT_COUNTRY_CODE);
+
+ mErrorUnsupportedFeatureSetCountryCode = true;
+ mThreadNetworkCountryCode.setOverrideCountryCode(TEST_COUNTRY_CODE_CN);
+ verify(mThreadNetworkControllerService)
+ .setCountryCode(eq(TEST_COUNTRY_CODE_CN), mOperationReceiverCaptor.capture());
+
+ mThreadNetworkCountryCode.setOverrideCountryCode(TEST_COUNTRY_CODE_US);
+ verifyNoMoreInteractions(mThreadNetworkControllerService);
+
+ assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(DEFAULT_COUNTRY_CODE);
+ }
+
+ @Test
+ public void setCountryCodeNotSupported_returnUnsupportedFeatureError_unregisterAllCallbacks() {
+ mThreadNetworkCountryCode.initialize();
+ assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(DEFAULT_COUNTRY_CODE);
+
+ mErrorUnsupportedFeatureSetCountryCode = true;
+ mThreadNetworkCountryCode.setOverrideCountryCode(TEST_COUNTRY_CODE_CN);
+ verify(mThreadNetworkControllerService)
+ .setCountryCode(eq(TEST_COUNTRY_CODE_CN), mOperationReceiverCaptor.capture());
+
+ verify(mLocationManager).removeUpdates(mLocationListenerCaptor.capture());
+ verify(mWifiManager)
+ .unregisterActiveCountryCodeChangedCallback(
+ mWifiCountryCodeReceiverCaptor.capture());
+ verify(mContext).unregisterReceiver(mTelephonyCountryCodeReceiverCaptor.capture());
+ }
+
+ @Test
public void settingsCountryCode_settingsCountryCodeIsActive_settingsCountryCodeIsUsed() {
when(mPersistentSettings.get(KEY_COUNTRY_CODE)).thenReturn(TEST_COUNTRY_CODE_CN);
mThreadNetworkCountryCode.initialize();
@@ -468,6 +507,7 @@
mThreadNetworkCountryCode.dump(new FileDescriptor(), printWriter, null);
String outputString = stringWriter.toString();
+ assertThat(outputString).contains("mIsCpSettingCountryCodeSupported");
assertThat(outputString).contains("mOverrideCountryCodeInfo");
assertThat(outputString).contains("mTelephonyCountryCodeSlotInfoMap");
assertThat(outputString).contains("mTelephonyCountryCodeInfo");