Merge changes from topic "rate-limit-trafficstats" into main
* changes:
Make cache duration and max entries configurable
Create TestRule: SetFeatureFlagsRule
Rate-limit on TrafficStats#get*[Bytes|Packets] API calls
Clear TrafficStats cache before getting readings from TrafficStats
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 83f798a..6d8ed4a 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -1,10 +1,6 @@
-chiachangwang@google.com
-cken@google.com
jchalard@google.com
junyulai@google.com
-lifr@google.com
lorenzo@google.com
-markchien@google.com
martinwu@google.com
maze@google.com
motomuman@google.com
@@ -12,7 +8,5 @@
prohr@google.com
reminv@google.com
satk@google.com
-waynema@google.com
xiaom@google.com
-yumike@google.com
yuyanghuang@google.com
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 544ba01..9e0c970 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -137,8 +137,8 @@
private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00");
private static final String TAG = "IpServer";
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
private static final Class[] sMessageClasses = {
IpServer.class
};
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 873961a..d85d92f 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -173,8 +173,8 @@
public class Tethering {
private static final String TAG = Tethering.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
private static final Class[] sMessageClasses = {
Tethering.class, TetherMainSM.class, IpServer.class
@@ -241,9 +241,6 @@
private final TetherMainSM mTetherMainSM;
private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
- // TODO: Figure out how to merge this and other downstream-tracking objects
- // into a single coherent structure.
- private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
private final EntitlementManager mEntitlementMgr;
@@ -271,8 +268,6 @@
private boolean mRndisEnabled; // track the RNDIS function enabled state
private boolean mNcmEnabled; // track the NCM function enabled state
- // True iff. WiFi tethering should be started when soft AP is ready.
- private boolean mWifiTetherRequested;
private Network mTetherUpstream;
private TetherStatesParcel mTetherStatesParcel;
private boolean mDataSaverEnabled = false;
@@ -329,7 +324,6 @@
(what, obj) -> {
mTetherMainSM.sendMessage(TetherMainSM.EVENT_UPSTREAM_CALLBACK, what, 0, obj);
});
- mForwardedDownstreams = new HashSet<>();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
@@ -763,7 +757,6 @@
}
if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))
|| (!enable && mgr.stopSoftAp())) {
- mWifiTetherRequested = enable;
return TETHER_ERROR_NO_ERROR;
}
} finally {
@@ -1470,10 +1463,6 @@
}
private void disableWifiIpServing(String ifname, int apState) {
- // Regardless of whether we requested this transition, the AP has gone
- // down. Don't try to tether again unless we're requested to do so.
- mWifiTetherRequested = false;
-
mLog.log("Canceling WiFi tethering request - interface=" + ifname + " state=" + apState);
disableWifiIpServingCommon(TETHERING_WIFI, ifname);
@@ -1505,8 +1494,7 @@
private void enableWifiIpServing(String ifname, int wifiIpMode) {
mLog.log("request WiFi tethering - interface=" + ifname + " state=" + wifiIpMode);
- // Map wifiIpMode values to IpServer.Callback serving states, inferring
- // from mWifiTetherRequested as a final "best guess".
+ // Map wifiIpMode values to IpServer.Callback serving states.
final int ipServingMode;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
@@ -1653,11 +1641,6 @@
mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
}
- private boolean upstreamWanted() {
- if (!mForwardedDownstreams.isEmpty()) return true;
- return mWifiTetherRequested;
- }
-
// Needed because the canonical source of upstream truth is just the
// upstream interface set, |mCurrentUpstreamIfaceSet|.
private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) {
@@ -1715,12 +1698,16 @@
private final ArrayList<IpServer> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
private final OffloadWrapper mOffload;
+ // TODO: Figure out how to merge this and other downstream-tracking objects
+ // into a single coherent structure.
+ private final HashSet<IpServer> mForwardedDownstreams;
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
TetherMainSM(String name, Looper looper, TetheringDependencies deps) {
super(name, looper);
+ mForwardedDownstreams = new HashSet<>();
mInitialState = new InitialState();
mTetherModeAliveState = new TetherModeAliveState();
mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState();
@@ -2056,6 +2043,10 @@
}
}
+ private boolean upstreamWanted() {
+ return !mForwardedDownstreams.isEmpty();
+ }
+
class TetherModeAliveState extends State {
boolean mUpstreamWanted = false;
boolean mTryCell = true;
@@ -2393,6 +2384,9 @@
hasCallingPermission(NETWORK_SETTINGS)
|| hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
|| hasCallingPermission(NETWORK_STACK);
+ if (callback == null) {
+ throw new NullPointerException();
+ }
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
@@ -2651,7 +2645,7 @@
}
pw.println(" - lastError = " + tetherState.lastError);
}
- pw.println("Upstream wanted: " + upstreamWanted());
+ pw.println("Upstream wanted: " + mTetherMainSM.upstreamWanted());
pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
pw.decreaseIndent();
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index f696885..120b871 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -74,7 +74,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import java.io.FileDescriptor;
import java.net.Inet4Address;
@@ -144,7 +143,7 @@
private static final Context sContext =
InstrumentationRegistry.getInstrumentation().getContext();
- private static final EthernetManager sEm = sContext.getSystemService(EthernetManager.class);
+ protected static final EthernetManager sEm = sContext.getSystemService(EthernetManager.class);
private static final TetheringManager sTm = sContext.getSystemService(TetheringManager.class);
private static final PackageManager sPackageManager = sContext.getPackageManager();
private static final CtsNetUtils sCtsNetUtils = new CtsNetUtils(sContext);
@@ -168,34 +167,6 @@
return sContext;
}
- @BeforeClass
- public static void setUpOnce() throws Exception {
- // The first test case may experience tethering restart with IP conflict handling.
- // Tethering would cache the last upstreams so that the next enabled tethering avoids
- // picking up the address that is in conflict with the upstreams. To protect subsequent
- // tests, turn tethering on and off before running them.
- MyTetheringEventCallback callback = null;
- TestNetworkInterface testIface = null;
- assumeTrue(sEm != null);
- try {
- // If the physical ethernet interface is available, do nothing.
- if (isInterfaceForTetheringAvailable()) return;
-
- testIface = createTestInterface();
- setIncludeTestInterfaces(true);
-
- callback = enableEthernetTethering(testIface.getInterfaceName(), null);
- callback.awaitUpstreamChanged(true /* throwTimeoutException */);
- } catch (TimeoutException e) {
- Log.d(TAG, "WARNNING " + e);
- } finally {
- maybeCloseTestInterface(testIface);
- maybeUnregisterTetheringEventCallback(callback);
-
- setIncludeTestInterfaces(false);
- }
- }
-
@Before
public void setUp() throws Exception {
mHandlerThread = new HandlerThread(getClass().getSimpleName());
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 4949eaa..c54d1b4 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -59,6 +59,7 @@
import com.android.testutils.NetworkStackModuleTest;
import com.android.testutils.TapPacketReader;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -150,6 +151,35 @@
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
};
+ /** Enable/disable tethering once before running the tests. */
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ // The first test case may experience tethering restart with IP conflict handling.
+ // Tethering would cache the last upstreams so that the next enabled tethering avoids
+ // picking up the address that is in conflict with the upstreams. To protect subsequent
+ // tests, turn tethering on and off before running them.
+ MyTetheringEventCallback callback = null;
+ TestNetworkInterface testIface = null;
+ assumeTrue(sEm != null);
+ try {
+ // If the physical ethernet interface is available, do nothing.
+ if (isInterfaceForTetheringAvailable()) return;
+
+ testIface = createTestInterface();
+ setIncludeTestInterfaces(true);
+
+ callback = enableEthernetTethering(testIface.getInterfaceName(), null);
+ callback.awaitUpstreamChanged(true /* throwTimeoutException */);
+ } catch (TimeoutException e) {
+ Log.d(TAG, "WARNNING " + e);
+ } finally {
+ maybeCloseTestInterface(testIface);
+ maybeUnregisterTetheringEventCallback(callback);
+
+ setIncludeTestInterfaces(false);
+ }
+ }
+
@Test
public void testVirtualEthernetAlreadyExists() throws Exception {
// This test requires manipulating packets. Skip if there is a physical Ethernet connected.
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 f01e1bb..9f430af 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -3653,10 +3653,9 @@
verify(mWifiManager).updateInterfaceIpState(TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
-
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
- // FIXME: wifi tethering doesn't have upstream when P2P is enabled.
- verify(mUpstreamNetworkMonitor, never()).setTryCell(true);
+
+ verify(mUpstreamNetworkMonitor).setTryCell(true);
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
diff --git a/framework-t/src/android/net/nsd/NsdServiceInfo.java b/framework-t/src/android/net/nsd/NsdServiceInfo.java
index dba08a1..9491a9c 100644
--- a/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -16,6 +16,8 @@
package android.net.nsd;
+import static com.android.net.module.util.HexDump.toHexString;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +41,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.StringJoiner;
/**
* A class representing service information for network service discovery
@@ -541,11 +544,50 @@
.append(", network: ").append(mNetwork)
.append(", expirationTime: ").append(mExpirationTime);
- byte[] txtRecord = getTxtRecord();
- sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
+ final StringJoiner txtJoiner =
+ new StringJoiner(", " /* delimiter */, "{" /* prefix */, "}" /* suffix */);
+
+ sb.append(", txtRecord: ");
+ for (int i = 0; i < mTxtRecord.size(); i++) {
+ txtJoiner.add(mTxtRecord.keyAt(i) + "=" + getPrintableTxtValue(mTxtRecord.valueAt(i)));
+ }
+ sb.append(txtJoiner.toString());
return sb.toString();
}
+ /**
+ * Returns printable string for {@code txtValue}.
+ *
+ * If {@code txtValue} contains non-printable ASCII characters, a HEX string with prefix "0x"
+ * will be returned. Otherwise, the ASCII string of {@code txtValue} is returned.
+ *
+ */
+ private static String getPrintableTxtValue(@Nullable byte[] txtValue) {
+ if (txtValue == null) {
+ return "(null)";
+ }
+
+ if (containsNonPrintableChars(txtValue)) {
+ return "0x" + toHexString(txtValue);
+ }
+
+ return new String(txtValue, StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Returns {@code true} if {@code txtValue} contains non-printable ASCII characters.
+ *
+ * The printable characters are in range of [32, 126].
+ */
+ private static boolean containsNonPrintableChars(byte[] txtValue) {
+ for (int i = 0; i < txtValue.length; i++) {
+ if (txtValue[i] < 32 || txtValue[i] > 126) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/** Implement the Parcelable interface */
public int describeContents() {
return 0;
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 85b1dac..45efbfe 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -749,22 +749,22 @@
* Network capabilities that are expected to be mutable, i.e., can change while a particular
* network is connected.
*/
- private static final long MUTABLE_CAPABILITIES = BitUtils.packBitList(
+ private static final long MUTABLE_CAPABILITIES =
// TRUSTED can change when user explicitly connects to an untrusted network in Settings.
// http://b/18206275
- NET_CAPABILITY_TRUSTED,
- NET_CAPABILITY_VALIDATED,
- NET_CAPABILITY_CAPTIVE_PORTAL,
- NET_CAPABILITY_NOT_ROAMING,
- NET_CAPABILITY_FOREGROUND,
- NET_CAPABILITY_NOT_CONGESTED,
- NET_CAPABILITY_NOT_SUSPENDED,
- NET_CAPABILITY_PARTIAL_CONNECTIVITY,
- NET_CAPABILITY_TEMPORARILY_NOT_METERED,
- NET_CAPABILITY_NOT_VCN_MANAGED,
+ (1L << NET_CAPABILITY_TRUSTED) |
+ (1L << NET_CAPABILITY_VALIDATED) |
+ (1L << NET_CAPABILITY_CAPTIVE_PORTAL) |
+ (1L << NET_CAPABILITY_NOT_ROAMING) |
+ (1L << NET_CAPABILITY_FOREGROUND) |
+ (1L << NET_CAPABILITY_NOT_CONGESTED) |
+ (1L << NET_CAPABILITY_NOT_SUSPENDED) |
+ (1L << NET_CAPABILITY_PARTIAL_CONNECTIVITY) |
+ (1L << NET_CAPABILITY_TEMPORARILY_NOT_METERED) |
+ (1L << NET_CAPABILITY_NOT_VCN_MANAGED) |
// The value of NET_CAPABILITY_HEAD_UNIT is 32, which cannot use int to do bit shift,
// otherwise there will be an overflow. Use long to do bit shift instead.
- NET_CAPABILITY_HEAD_UNIT);
+ (1L << NET_CAPABILITY_HEAD_UNIT);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -784,10 +784,10 @@
/**
* Capabilities that are set by default when the object is constructed.
*/
- private static final long DEFAULT_CAPABILITIES = BitUtils.packBitList(
- NET_CAPABILITY_NOT_RESTRICTED,
- NET_CAPABILITY_TRUSTED,
- NET_CAPABILITY_NOT_VPN);
+ private static final long DEFAULT_CAPABILITIES =
+ (1L << NET_CAPABILITY_NOT_RESTRICTED) |
+ (1L << NET_CAPABILITY_TRUSTED) |
+ (1L << NET_CAPABILITY_NOT_VPN);
/**
* Capabilities that are managed by ConnectivityService.
@@ -795,11 +795,10 @@
*/
@VisibleForTesting
public static final long CONNECTIVITY_MANAGED_CAPABILITIES =
- BitUtils.packBitList(
- NET_CAPABILITY_VALIDATED,
- NET_CAPABILITY_CAPTIVE_PORTAL,
- NET_CAPABILITY_FOREGROUND,
- NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+ (1L << NET_CAPABILITY_VALIDATED) |
+ (1L << NET_CAPABILITY_CAPTIVE_PORTAL) |
+ (1L << NET_CAPABILITY_FOREGROUND) |
+ (1L << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
* Capabilities that are allowed for all test networks. This list must be set so that it is safe
@@ -808,15 +807,14 @@
* IMS, SUPL, etc.
*/
private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
- BitUtils.packBitList(
- NET_CAPABILITY_NOT_METERED,
- NET_CAPABILITY_TEMPORARILY_NOT_METERED,
- NET_CAPABILITY_NOT_RESTRICTED,
- NET_CAPABILITY_NOT_VPN,
- NET_CAPABILITY_NOT_ROAMING,
- NET_CAPABILITY_NOT_CONGESTED,
- NET_CAPABILITY_NOT_SUSPENDED,
- NET_CAPABILITY_NOT_VCN_MANAGED);
+ (1L << NET_CAPABILITY_NOT_METERED) |
+ (1L << NET_CAPABILITY_TEMPORARILY_NOT_METERED) |
+ (1L << NET_CAPABILITY_NOT_RESTRICTED) |
+ (1L << NET_CAPABILITY_NOT_VPN) |
+ (1L << NET_CAPABILITY_NOT_ROAMING) |
+ (1L << NET_CAPABILITY_NOT_CONGESTED) |
+ (1L << NET_CAPABILITY_NOT_SUSPENDED) |
+ (1L << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Extra allowed capabilities for test networks that do not have TRANSPORT_CELLULAR. Test
@@ -824,7 +822,9 @@
* the risk of being used by running apps.
*/
private static final long TEST_NETWORKS_EXTRA_ALLOWED_CAPABILITIES_ON_NON_CELL =
- BitUtils.packBitList(NET_CAPABILITY_CBS, NET_CAPABILITY_DUN, NET_CAPABILITY_RCS);
+ (1L << NET_CAPABILITY_CBS) |
+ (1L << NET_CAPABILITY_DUN) |
+ (1L << NET_CAPABILITY_RCS);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -1174,7 +1174,7 @@
* @hide
*/
public void maybeMarkCapabilitiesRestricted() {
- if (NetworkCapabilitiesUtils.inferRestrictedCapability(this)) {
+ if (NetworkCapabilitiesUtils.inferRestrictedCapability(mNetworkCapabilities)) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
}
@@ -1368,12 +1368,11 @@
* Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
*/
private static final long UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
- BitUtils.packBitList(
- TRANSPORT_TEST,
- // Test eth networks are created with EthernetManager#setIncludeTestInterfaces
- TRANSPORT_ETHERNET,
- // Test VPN networks can be created but their UID ranges must be empty.
- TRANSPORT_VPN);
+ (1L << TRANSPORT_TEST) |
+ // Test eth networks are created with EthernetManager#setIncludeTestInterfaces
+ (1L << TRANSPORT_ETHERNET) |
+ // Test VPN networks can be created but their UID ranges must be empty.
+ (1L << TRANSPORT_VPN);
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index aca386f..8552eec 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -275,7 +275,7 @@
}
@VisibleForTesting
- static class MdnsListener implements MdnsServiceBrowserListener {
+ abstract static class MdnsListener implements MdnsServiceBrowserListener {
protected final int mClientRequestId;
protected final int mTransactionId;
@NonNull
@@ -321,6 +321,10 @@
@Override
public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
+
+ // Ensure toString gets overridden
+ @NonNull
+ public abstract String toString();
}
private class DiscoveryListener extends MdnsListener {
@@ -351,13 +355,21 @@
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("DiscoveryListener: serviceType=%s", getListenedServiceType());
+ }
}
private class ResolutionListener extends MdnsListener {
+ private final String mServiceName;
ResolutionListener(int clientRequestId, int transactionId,
- @NonNull String listenServiceType) {
+ @NonNull String listenServiceType, @NonNull String serviceName) {
super(clientRequestId, transactionId, listenServiceType);
+ mServiceName = serviceName;
}
@Override
@@ -373,13 +385,22 @@
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("ResolutionListener serviceName=%s, serviceType=%s",
+ mServiceName, getListenedServiceType());
+ }
}
private class ServiceInfoListener extends MdnsListener {
+ private final String mServiceName;
ServiceInfoListener(int clientRequestId, int transactionId,
- @NonNull String listenServiceType) {
+ @NonNull String listenServiceType, @NonNull String serviceName) {
super(clientRequestId, transactionId, listenServiceType);
+ this.mServiceName = serviceName;
}
@Override
@@ -410,6 +431,13 @@
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("ServiceInfoListener serviceName=%s, serviceType=%s",
+ mServiceName, getListenedServiceType());
+ }
}
private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
@@ -656,9 +684,12 @@
}
private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
- ClientInfo clientInfo, @Nullable Network requestedNetwork) {
+ ClientInfo clientInfo, @NonNull NsdServiceInfo serviceInfo) {
+ final String serviceFullName =
+ serviceInfo.getServiceName() + "." + serviceInfo.getServiceType();
clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
- transactionId, requestedNetwork, mClock.elapsedRealtime()));
+ transactionId, serviceInfo.getNetwork(), serviceFullName,
+ mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
updateMulticastLock();
}
@@ -1010,7 +1041,7 @@
mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
mdnsAdvertisingOptions, clientInfo.mUid);
storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
- serviceInfo.getNetwork());
+ serviceInfo);
} else {
maybeStartDaemon();
transactionId = getUniqueId();
@@ -1104,9 +1135,12 @@
maybeStartMonitoringSockets();
final MdnsListener listener = new ResolutionListener(clientRequestId,
- transactionId, resolveServiceType);
+ transactionId, resolveServiceType, info.getServiceName());
+ final int ifaceIdx = info.getNetwork() != null
+ ? 0 : info.getInterfaceIndex();
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
+ .setInterfaceIndex(ifaceIdx)
.setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
? AGGRESSIVE_QUERY_MODE
: PASSIVE_QUERY_MODE)
@@ -1204,9 +1238,12 @@
maybeStartMonitoringSockets();
final MdnsListener listener = new ServiceInfoListener(clientRequestId,
- transactionId, resolveServiceType);
+ transactionId, resolveServiceType, info.getServiceName());
+ final int ifIndex = info.getNetwork() != null
+ ? 0 : info.getInterfaceIndex();
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
+ .setInterfaceIndex(ifIndex)
.setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
? AGGRESSIVE_QUERY_MODE
: PASSIVE_QUERY_MODE)
@@ -2545,6 +2582,17 @@
// Dump state machine logs
mNsdStateMachine.dump(fd, pw, args);
+ // Dump clients
+ pw.println();
+ pw.println("Active clients:");
+ pw.increaseIndent();
+ HandlerUtils.runWithScissorsForDump(mNsdStateMachine.getHandler(), () -> {
+ for (ClientInfo clientInfo : mClients.values()) {
+ pw.println(clientInfo.toString());
+ }
+ }, 10_000);
+ pw.decreaseIndent();
+
// Dump service and clients logs
pw.println();
pw.println("Logs:");
@@ -2617,6 +2665,21 @@
public int getSentQueryCount() {
return mSentQueryCount;
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return getRequestDescriptor() + " {" + mTransactionId
+ + ", startTime " + mStartTimeMs
+ + ", foundServices " + mFoundServiceCount
+ + ", lostServices " + mLostServiceCount
+ + ", fromCache " + mIsServiceFromCache
+ + ", sentQueries " + mSentQueryCount
+ + "}";
+ }
+
+ @NonNull
+ protected abstract String getRequestDescriptor();
}
private static class LegacyClientRequest extends ClientRequest {
@@ -2626,6 +2689,12 @@
super(transactionId, startTimeMs);
mRequestCode = requestCode;
}
+
+ @NonNull
+ @Override
+ protected String getRequestDescriptor() {
+ return "Legacy (" + mRequestCode + ")";
+ }
}
private abstract static class JavaBackendClientRequest extends ClientRequest {
@@ -2645,9 +2714,20 @@
}
private static class AdvertiserClientRequest extends JavaBackendClientRequest {
+ @NonNull
+ private final String mServiceFullName;
+
private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
- long startTimeMs) {
+ @NonNull String serviceFullName, long startTimeMs) {
super(transactionId, requestedNetwork, startTimeMs);
+ mServiceFullName = serviceFullName;
+ }
+
+ @NonNull
+ @Override
+ public String getRequestDescriptor() {
+ return String.format("Advertiser: serviceFullName=%s, net=%s",
+ mServiceFullName, getRequestedNetwork());
}
}
@@ -2660,6 +2740,12 @@
super(transactionId, requestedNetwork, startTimeMs);
mListener = listener;
}
+
+ @NonNull
+ @Override
+ public String getRequestDescriptor() {
+ return String.format("Discovery/%s, net=%s", mListener, getRequestedNetwork());
+ }
}
/* Information tracked per client */
@@ -2704,17 +2790,15 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("mResolvedService ").append(mResolvedService).append("\n");
- sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
- sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
- sb.append("mUid ").append(mUid).append("\n");
+ sb.append("mUid ").append(mUid).append(", ");
+ sb.append("mResolvedService ").append(mResolvedService).append(", ");
+ sb.append("mIsLegacy ").append(mIsPreSClient).append(", ");
+ sb.append("mUseJavaBackend ").append(mUseJavaBackend).append(", ");
+ sb.append("mClientRequests:\n");
for (int i = 0; i < mClientRequests.size(); i++) {
int clientRequestId = mClientRequests.keyAt(i);
- sb.append("clientRequestId ")
- .append(clientRequestId)
- .append(" transactionId ").append(mClientRequests.valueAt(i).mTransactionId)
- .append(" type ").append(
- mClientRequests.valueAt(i).getClass().getSimpleName())
+ sb.append(" ").append(clientRequestId)
+ .append(": ").append(mClientRequests.valueAt(i).toString())
.append("\n");
}
return sb.toString();
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index 7b0c738..0ab7a76 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -241,11 +241,30 @@
}
}
// Request the network for discovery.
+ // This requests sockets on all networks even if the searchOptions have a given interface
+ // index (with getNetwork==null, for local interfaces), and only uses matching interfaces
+ // in that case. While this is a simple solution to only use matching sockets, a better
+ // practice would be to only request the correct socket for discovery.
+ // TODO: avoid requesting extra sockets after migrating P2P and tethering networks to local
+ // NetworkAgents.
socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork(),
new MdnsSocketClientBase.SocketCreationCallback() {
@Override
public void onSocketCreated(@NonNull SocketKey socketKey) {
discoveryExecutor.ensureRunningOnHandlerThread();
+ final int searchInterfaceIndex = searchOptions.getInterfaceIndex();
+ if (searchOptions.getNetwork() == null
+ && searchInterfaceIndex > 0
+ // The interface index in options should only match interfaces that
+ // do not have any Network; a matching Network should be provided
+ // otherwise.
+ && (socketKey.getNetwork() != null
+ || socketKey.getInterfaceIndex() != searchInterfaceIndex)) {
+ sharedLog.i("Skipping " + socketKey + " as ifIndex "
+ + searchInterfaceIndex + " was requested.");
+ return;
+ }
+
// All listeners of the same service types shares the same
// MdnsServiceTypeClient.
MdnsServiceTypeClient serviceTypeClient =
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java
index 086094b..73405ab 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java
@@ -59,6 +59,7 @@
source.readInt(),
source.readInt() == 1,
source.readParcelable(null),
+ source.readInt(),
source.readString(),
source.readInt() == 1,
source.readInt());
@@ -79,6 +80,8 @@
private final boolean removeExpiredService;
// The target network for searching. Null network means search on all possible interfaces.
@Nullable private final Network mNetwork;
+ // If the target interface does not have a Network, set to the interface index, otherwise unset.
+ private final int mInterfaceIndex;
/** Parcelable constructs for a {@link MdnsSearchOptions}. */
MdnsSearchOptions(
@@ -86,6 +89,7 @@
int queryMode,
boolean removeExpiredService,
@Nullable Network network,
+ int interfaceIndex,
@Nullable String resolveInstanceName,
boolean onlyUseIpv6OnIpv6OnlyNetworks,
int numOfQueriesBeforeBackoff) {
@@ -98,6 +102,7 @@
this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff;
this.removeExpiredService = removeExpiredService;
mNetwork = network;
+ mInterfaceIndex = interfaceIndex;
this.resolveInstanceName = resolveInstanceName;
}
@@ -148,15 +153,27 @@
}
/**
- * Returns the network which the mdns query should target on.
+ * Returns the network which the mdns query should target.
*
- * @return the target network or null if search on all possible interfaces.
+ * @return the target network or null to search on all possible interfaces.
*/
@Nullable
public Network getNetwork() {
return mNetwork;
}
+
+ /**
+ * Returns the interface index which the mdns query should target.
+ *
+ * This is only set when the service is to be searched on an interface that does not have a
+ * Network, in which case {@link #getNetwork()} returns null.
+ * The interface index as per {@link java.net.NetworkInterface#getIndex}, or 0 if unset.
+ */
+ public int getInterfaceIndex() {
+ return mInterfaceIndex;
+ }
+
/**
* If non-null, queries should try to resolve all records of this specific service, rather than
* discovering all services.
@@ -177,6 +194,7 @@
out.writeInt(queryMode);
out.writeInt(removeExpiredService ? 1 : 0);
out.writeParcelable(mNetwork, 0);
+ out.writeInt(mInterfaceIndex);
out.writeString(resolveInstanceName);
out.writeInt(onlyUseIpv6OnIpv6OnlyNetworks ? 1 : 0);
out.writeInt(numOfQueriesBeforeBackoff);
@@ -190,6 +208,7 @@
private int numOfQueriesBeforeBackoff = 3;
private boolean removeExpiredService;
private Network mNetwork;
+ private int mInterfaceIndex;
private String resolveInstanceName;
private Builder() {
@@ -278,6 +297,16 @@
return this;
}
+ /**
+ * Set the interface index to use for the query, if not querying on a {@link Network}.
+ *
+ * @see MdnsSearchOptions#getInterfaceIndex()
+ */
+ public Builder setInterfaceIndex(int index) {
+ mInterfaceIndex = index;
+ return this;
+ }
+
/** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */
public MdnsSearchOptions build() {
return new MdnsSearchOptions(
@@ -285,6 +314,7 @@
queryMode,
removeExpiredService,
mNetwork,
+ mInterfaceIndex,
resolveInstanceName,
onlyUseIpv6OnIpv6OnlyNetworks,
numOfQueriesBeforeBackoff);
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 0b54fdd..cadc04d 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -250,6 +250,17 @@
return mTrackingInterfaces.containsKey(ifaceName);
}
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @Nullable
+ protected String getHwAddress(@NonNull final String ifaceName) {
+ if (!hasInterface(ifaceName)) {
+ return null;
+ }
+
+ NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
+ return iface.mHwAddress;
+ }
+
@VisibleForTesting
static class NetworkInterfaceState {
final String name;
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 9c8fd99..71f289e 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -131,6 +131,10 @@
// returned when a tethered interface is requested; until then, it remains in client mode. Its
// current mode is reflected in mTetheringInterfaceMode.
private String mTetheringInterface;
+ // If the tethering interface is in server mode, it is not tracked by factory. The HW address
+ // must be maintained by the EthernetTracker. Its current mode is reflected in
+ // mTetheringInterfaceMode.
+ private String mTetheringInterfaceHwAddr;
private int mTetheringInterfaceMode = INTERFACE_MODE_CLIENT;
// Tracks whether clients were notified that the tethered interface is available
private boolean mTetheredInterfaceWasAvailable = false;
@@ -582,6 +586,7 @@
removeInterface(iface);
if (iface.equals(mTetheringInterface)) {
mTetheringInterface = null;
+ mTetheringInterfaceHwAddr = null;
}
broadcastInterfaceStateChange(iface);
}
@@ -610,13 +615,14 @@
return;
}
+ final String hwAddress = config.hwAddr;
+
if (getInterfaceMode(iface) == INTERFACE_MODE_SERVER) {
maybeUpdateServerModeInterfaceState(iface, true);
+ mTetheringInterfaceHwAddr = hwAddress;
return;
}
- final String hwAddress = config.hwAddr;
-
NetworkCapabilities nc = mNetworkCapabilities.get(iface);
if (nc == null) {
// Try to resolve using mac address
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 005d617..b1ae019 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -478,6 +478,7 @@
private volatile boolean mLockdownEnabled;
private final boolean mRequestRestrictedWifiEnabled;
+ private final boolean mBackgroundFirewallChainEnabled;
/**
* Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in
@@ -1798,6 +1799,8 @@
mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
mRequestRestrictedWifiEnabled = mDeps.isAtLeastU()
&& mDeps.isFeatureEnabled(context, REQUEST_RESTRICTED_WIFI);
+ mBackgroundFirewallChainEnabled = mDeps.isAtLeastV() && mDeps.isFeatureNotChickenedOut(
+ context, ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN);
mCarrierPrivilegeAuthenticator = mDeps.makeCarrierPrivilegeAuthenticator(
mContext, mTelephonyManager, mRequestRestrictedWifiEnabled,
this::handleUidCarrierPrivilegesLost, mHandler);
@@ -4152,6 +4155,9 @@
pw.println();
pw.println("Multicast routing supported: " +
(mMulticastRoutingCoordinatorService != null));
+
+ pw.println();
+ pw.println("Background firewall chain enabled: " + mBackgroundFirewallChainEnabled);
}
private void dumpNetworks(IndentingPrintWriter pw) {
@@ -13521,6 +13527,12 @@
public void setUidFirewallRule(final int chain, final int uid, final int rule) {
enforceNetworkStackOrSettingsPermission();
+ if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) {
+ Log.i(TAG, "Ignoring operation setUidFirewallRule on the background chain because the"
+ + " feature is disabled.");
+ return;
+ }
+
// There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
int firewallRule = getFirewallRuleType(chain, rule);
@@ -13593,6 +13605,12 @@
public void setFirewallChainEnabled(final int chain, final boolean enable) {
enforceNetworkStackOrSettingsPermission();
+ if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) {
+ Log.i(TAG, "Ignoring operation setFirewallChainEnabled on the background chain because"
+ + " the feature is disabled.");
+ return;
+ }
+
try {
mBpfNetMaps.setChildChain(chain, enable);
} catch (ServiceSpecificException e) {
@@ -13619,6 +13637,12 @@
public void replaceFirewallChain(final int chain, final int[] uids) {
enforceNetworkStackOrSettingsPermission();
+ if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) {
+ Log.i(TAG, "Ignoring operation replaceFirewallChain on the background chain because"
+ + " the feature is disabled.");
+ return;
+ }
+
mBpfNetMaps.replaceUidChain(chain, uids);
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index a55c683..176307d 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -42,6 +42,8 @@
public static final String INGRESS_TO_VPN_ADDRESS_FILTERING =
"ingress_to_vpn_address_filtering";
+ public static final String BACKGROUND_FIREWALL_CHAIN = "background_firewall_chain";
+
private boolean mNoRematchAllRequestsOnRegister;
/**
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NduseroptMessage.java b/staticlibs/device/com/android/net/module/util/netlink/NduseroptMessage.java
index 2e9a99b..586f19d 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NduseroptMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NduseroptMessage.java
@@ -156,8 +156,9 @@
@Override
public String toString() {
- return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
+ return String.format("Nduseroptmsg(family:%d, opts_len:%d, ifindex:%d, icmp_type:%d, "
+ + "icmp_code:%d, srcaddr: %s, %s)",
family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
- Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
+ Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress(), option);
}
}
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
index fa07885..7066131 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
@@ -45,7 +45,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import static com.android.net.module.util.BitUtils.packBitList;
+import static com.android.net.module.util.BitUtils.packBits;
import static com.android.net.module.util.BitUtils.unpackBits;
import android.annotation.NonNull;
@@ -89,41 +89,41 @@
* and {@code FORCE_RESTRICTED_CAPABILITIES}.
*/
@VisibleForTesting
- public static final long RESTRICTED_CAPABILITIES = packBitList(
- NET_CAPABILITY_BIP,
- NET_CAPABILITY_CBS,
- NET_CAPABILITY_DUN,
- NET_CAPABILITY_EIMS,
- NET_CAPABILITY_ENTERPRISE,
- NET_CAPABILITY_FOTA,
- NET_CAPABILITY_IA,
- NET_CAPABILITY_IMS,
- NET_CAPABILITY_MCX,
- NET_CAPABILITY_RCS,
- NET_CAPABILITY_VEHICLE_INTERNAL,
- NET_CAPABILITY_VSIM,
- NET_CAPABILITY_XCAP,
- NET_CAPABILITY_MMTEL);
+ public static final long RESTRICTED_CAPABILITIES =
+ (1L << NET_CAPABILITY_BIP) |
+ (1L << NET_CAPABILITY_CBS) |
+ (1L << NET_CAPABILITY_DUN) |
+ (1L << NET_CAPABILITY_EIMS) |
+ (1L << NET_CAPABILITY_ENTERPRISE) |
+ (1L << NET_CAPABILITY_FOTA) |
+ (1L << NET_CAPABILITY_IA) |
+ (1L << NET_CAPABILITY_IMS) |
+ (1L << NET_CAPABILITY_MCX) |
+ (1L << NET_CAPABILITY_RCS) |
+ (1L << NET_CAPABILITY_VEHICLE_INTERNAL) |
+ (1L << NET_CAPABILITY_VSIM) |
+ (1L << NET_CAPABILITY_XCAP) |
+ (1L << NET_CAPABILITY_MMTEL);
/**
* Capabilities that force network to be restricted.
* See {@code NetworkCapabilities#maybeMarkCapabilitiesRestricted}.
*/
- private static final long FORCE_RESTRICTED_CAPABILITIES = packBitList(
- NET_CAPABILITY_ENTERPRISE,
- NET_CAPABILITY_OEM_PAID,
- NET_CAPABILITY_OEM_PRIVATE);
+ private static final long FORCE_RESTRICTED_CAPABILITIES =
+ (1L << NET_CAPABILITY_ENTERPRISE) |
+ (1L << NET_CAPABILITY_OEM_PAID) |
+ (1L << NET_CAPABILITY_OEM_PRIVATE);
/**
* Capabilities that suggest that a network is unrestricted.
* See {@code NetworkCapabilities#maybeMarkCapabilitiesRestricted}.
*/
@VisibleForTesting
- public static final long UNRESTRICTED_CAPABILITIES = packBitList(
- NET_CAPABILITY_INTERNET,
- NET_CAPABILITY_MMS,
- NET_CAPABILITY_SUPL,
- NET_CAPABILITY_WIFI_P2P);
+ public static final long UNRESTRICTED_CAPABILITIES =
+ (1L << NET_CAPABILITY_INTERNET) |
+ (1L << NET_CAPABILITY_MMS) |
+ (1L << NET_CAPABILITY_SUPL) |
+ (1L << NET_CAPABILITY_WIFI_P2P);
/**
* Get a transport that can be used to classify a network when displaying its info to users.
@@ -159,28 +159,33 @@
*
* @return {@code true} if the network should be restricted.
*/
- // TODO: Use packBits(nc.getCapabilities()) to check more easily using bit masks.
public static boolean inferRestrictedCapability(NetworkCapabilities nc) {
+ return inferRestrictedCapability(packBits(nc.getCapabilities()));
+ }
+
+ /**
+ * Infers that all the capabilities it provides are typically provided by restricted networks
+ * or not.
+ *
+ * @param capabilities see {@link NetworkCapabilities#getCapabilities()}
+ *
+ * @return {@code true} if the network should be restricted.
+ */
+ public static boolean inferRestrictedCapability(long capabilities) {
// Check if we have any capability that forces the network to be restricted.
- for (int capability : unpackBits(FORCE_RESTRICTED_CAPABILITIES)) {
- if (nc.hasCapability(capability)) {
- return true;
- }
+ if ((capabilities & FORCE_RESTRICTED_CAPABILITIES) != 0) {
+ return true;
}
// Verify there aren't any unrestricted capabilities. If there are we say
// the whole thing is unrestricted unless it is forced to be restricted.
- for (int capability : unpackBits(UNRESTRICTED_CAPABILITIES)) {
- if (nc.hasCapability(capability)) {
- return false;
- }
+ if ((capabilities & UNRESTRICTED_CAPABILITIES) != 0) {
+ return false;
}
// Must have at least some restricted capabilities.
- for (int capability : unpackBits(RESTRICTED_CAPABILITIES)) {
- if (nc.hasCapability(capability)) {
- return true;
- }
+ if ((capabilities & RESTRICTED_CAPABILITIES) != 0) {
+ return true;
}
return false;
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NduseroptMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NduseroptMessageTest.java
index 4fc5ec2..d1df3a6 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NduseroptMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NduseroptMessageTest.java
@@ -252,8 +252,10 @@
public void testToString() {
NduseroptMessage msg = parseNduseroptMessage(toBuffer(MSG_PREF64));
assertNotNull(msg);
- assertEquals("Nduseroptmsg(10, 16, 1431655765, 134, 0, fe80:2:3:4:5:6:7:8%1431655765)",
- msg.toString());
+ final String expected = "Nduseroptmsg(family:10, opts_len:16, ifindex:1431655765, "
+ + "icmp_type:134, icmp_code:0, srcaddr: fe80:2:3:4:5:6:7:8%1431655765, "
+ + "NdOptPref64(2001:db8:3:4:5:6::/96, 10064))";
+ assertEquals(expected, msg.toString());
}
// Convenience method to parse a NduseroptMessage that's not part of a netlink message.
diff --git a/staticlibs/testutils/app/connectivitychecker/Android.bp b/staticlibs/testutils/app/connectivitychecker/Android.bp
index 5af8c14..394c6be 100644
--- a/staticlibs/testutils/app/connectivitychecker/Android.bp
+++ b/staticlibs/testutils/app/connectivitychecker/Android.bp
@@ -30,7 +30,6 @@
"modules-utils-build_system",
"net-tests-utils",
],
- host_required: ["net-tests-utils-host-common"],
lint: {
strict_updatability_linting: true,
},
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt
new file mode 100644
index 0000000..93422ad
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testutils
+
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import android.os.Handler
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.testutils.RecorderCallback.CallbackEntry
+import java.util.Collections
+import kotlin.test.fail
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A rule to file [NetworkCallback]s to request or watch networks.
+ *
+ * The callbacks filed in test methods are automatically unregistered when the method completes.
+ */
+class AutoReleaseNetworkCallbackRule : NetworkCallbackHelper(), TestRule {
+ override fun apply(base: Statement, description: Description): Statement {
+ return RequestCellNetworkStatement(base, description)
+ }
+
+ private inner class RequestCellNetworkStatement(
+ private val base: Statement,
+ private val description: Description
+ ) : Statement() {
+ override fun evaluate() {
+ tryTest {
+ base.evaluate()
+ } cleanup {
+ unregisterAll()
+ }
+ }
+ }
+}
+
+/**
+ * Helps file [NetworkCallback]s to request or watch networks, keeping track of them for cleanup.
+ */
+open class NetworkCallbackHelper {
+ private val cm by lazy {
+ InstrumentationRegistry.getInstrumentation().context
+ .getSystemService(ConnectivityManager::class.java)
+ ?: fail("ConnectivityManager not found")
+ }
+ private val cbToCleanup = Collections.synchronizedSet(mutableSetOf<NetworkCallback>())
+ private var cellRequestCb: TestableNetworkCallback? = null
+
+ /**
+ * Convenience method to request a cell network, similarly to [requestNetwork].
+ *
+ * The rule will keep tract of a single cell network request, which can be unrequested manually
+ * using [unrequestCell].
+ */
+ fun requestCell(): Network {
+ if (cellRequestCb != null) {
+ fail("Cell network was already requested")
+ }
+ val cb = requestNetwork(
+ NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build()
+ )
+ cellRequestCb = cb
+ return cb.expect<CallbackEntry.Available>(
+ errorMsg = "Cell network not available. " +
+ "Please ensure the device has working mobile data."
+ ).network
+ }
+
+ /**
+ * Unrequest a cell network requested through [requestCell].
+ */
+ fun unrequestCell() {
+ val cb = cellRequestCb ?: fail("Cell network was not requested")
+ unregisterNetworkCallback(cb)
+ cellRequestCb = null
+ }
+
+ private fun addCallback(
+ cb: TestableNetworkCallback,
+ registrar: (TestableNetworkCallback) -> Unit
+ ): TestableNetworkCallback {
+ registrar(cb)
+ cbToCleanup.add(cb)
+ return cb
+ }
+
+ /**
+ * File a request for a Network.
+ *
+ * This will fail tests (throw) if the cell network cannot be obtained, or if it was already
+ * requested.
+ *
+ * Tests may call [unregisterNetworkCallback] once they are done using the returned [Network],
+ * otherwise it will be automatically unrequested after the test.
+ */
+ @JvmOverloads
+ fun requestNetwork(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler? = null
+ ) = addCallback(cb) {
+ if (handler == null) {
+ cm.requestNetwork(request, it)
+ } else {
+ cm.requestNetwork(request, it, handler)
+ }
+ }
+
+ /**
+ * Overload of [requestNetwork] that allows specifying a timeout.
+ */
+ @JvmOverloads
+ fun requestNetwork(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ timeoutMs: Int,
+ ) = addCallback(cb) { cm.requestNetwork(request, it, timeoutMs) }
+
+ /**
+ * File a callback for a NetworkRequest.
+ *
+ * This will fail tests (throw) if the cell network cannot be obtained, or if it was already
+ * requested.
+ *
+ * Tests may call [unregisterNetworkCallback] once they are done using the returned [Network],
+ * otherwise it will be automatically unrequested after the test.
+ */
+ @JvmOverloads
+ fun registerNetworkCallback(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback()
+ ) = addCallback(cb) { cm.registerNetworkCallback(request, it) }
+
+ /**
+ * @see ConnectivityManager.registerDefaultNetworkCallback
+ */
+ @JvmOverloads
+ fun registerDefaultNetworkCallback(
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler? = null
+ ) = addCallback(cb) {
+ if (handler == null) {
+ cm.registerDefaultNetworkCallback(it)
+ } else {
+ cm.registerDefaultNetworkCallback(it, handler)
+ }
+ }
+
+ /**
+ * @see ConnectivityManager.registerSystemDefaultNetworkCallback
+ */
+ @JvmOverloads
+ fun registerSystemDefaultNetworkCallback(
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerSystemDefaultNetworkCallback(it, handler) }
+
+ /**
+ * @see ConnectivityManager.registerDefaultNetworkCallbackForUid
+ */
+ @JvmOverloads
+ fun registerDefaultNetworkCallbackForUid(
+ uid: Int,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerDefaultNetworkCallbackForUid(uid, it, handler) }
+
+ /**
+ * @see ConnectivityManager.registerBestMatchingNetworkCallback
+ */
+ @JvmOverloads
+ fun registerBestMatchingNetworkCallback(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerBestMatchingNetworkCallback(request, it, handler) }
+
+ /**
+ * @see ConnectivityManager.requestBackgroundNetwork
+ */
+ @JvmOverloads
+ fun requestBackgroundNetwork(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.requestBackgroundNetwork(request, it, handler) }
+
+ /**
+ * Unregister a callback filed using registration methods in this class.
+ */
+ fun unregisterNetworkCallback(cb: NetworkCallback) {
+ cm.unregisterNetworkCallback(cb)
+ cbToCleanup.remove(cb)
+ }
+
+ /**
+ * Unregister all callbacks that were filed using registration methods in this class.
+ */
+ fun unregisterAll() {
+ cbToCleanup.forEach { cm.unregisterNetworkCallback(it) }
+ cbToCleanup.clear()
+ cellRequestCb = null
+ }
+}
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index cb4ca59..acf506d 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -9,4 +9,6 @@
# For incremental changes on EthernetManagerTest to increase coverage for existing behavior and for
# testing bug fixes.
per-file net/src/android/net/cts/EthernetManagerTest.kt = prohr@google.com #{LAST_RESORT_SUGGESTION}
+# Temporary ownership to develop APF CTS tests.
+per-file net/src/android/net/cts/ApfIntegrationTest.kt = prohr@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/tests/cts/hostside/aidl/Android.bp b/tests/cts/hostside/aidl/Android.bp
index 18a5897..33761dc 100644
--- a/tests/cts/hostside/aidl/Android.bp
+++ b/tests/cts/hostside/aidl/Android.bp
@@ -21,9 +21,7 @@
name: "CtsHostsideNetworkTestsAidl",
sdk_version: "current",
srcs: [
- "com/android/cts/net/hostside/IMyService.aidl",
- "com/android/cts/net/hostside/INetworkCallback.aidl",
- "com/android/cts/net/hostside/INetworkStateObserver.aidl",
- "com/android/cts/net/hostside/IRemoteSocketFactory.aidl",
+ "com/android/cts/net/hostside/*.aidl",
+ "com/android/cts/net/hostside/*.java",
],
}
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
index e7b2815..906024b 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -19,11 +19,12 @@
import android.app.job.JobInfo;
import com.android.cts.net.hostside.INetworkCallback;
+import com.android.cts.net.hostside.NetworkCheckResult;
interface IMyService {
void registerBroadcastReceiver();
int getCounters(String receiverName, String action);
- String checkNetworkStatus();
+ NetworkCheckResult checkNetworkStatus(String customUrl);
String getRestrictBackgroundStatus();
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in NetworkRequest request, in INetworkCallback cb);
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
index 19198c5..8ef4659 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
@@ -16,8 +16,12 @@
package com.android.cts.net.hostside;
+import android.net.NetworkInfo;
+
+import com.android.cts.net.hostside.NetworkCheckResult;
+
interface INetworkStateObserver {
- void onNetworkStateChecked(int resultCode, String resultData);
+ void onNetworkStateChecked(int resultCode, in NetworkCheckResult networkCheckResult);
const int RESULT_SUCCESS_NETWORK_STATE_CHECKED = 0;
const int RESULT_ERROR_UNEXPECTED_PROC_STATE = 1;
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl
new file mode 100644
index 0000000..cdd6b70
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.net.NetworkInfo;
+
+@JavaDerive(toString=true)
+parcelable NetworkCheckResult {
+ boolean connected;
+ String details;
+ NetworkInfo networkInfo;
+}
\ No newline at end of file
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 2ca8832..4437986 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -23,6 +23,7 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
@@ -51,6 +52,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
@@ -78,10 +80,10 @@
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
/**
@@ -136,6 +138,7 @@
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
+ private static final String KEY_CUSTOM_URL = TEST_PKG + ".custom_url";
private static final String EMPTY_STRING = "";
@@ -165,6 +168,7 @@
protected ConnectivityManager mCm;
protected int mUid;
private int mMyUid;
+ private @Nullable String mCustomUrl;
private MyServiceClient mServiceClient;
private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper;
private PowerManager mPowerManager;
@@ -185,6 +189,11 @@
mServiceClient = new MyServiceClient(mContext);
final Bundle args = InstrumentationRegistry.getArguments();
+ mCustomUrl = args.getString(ARG_CONNECTION_CHECK_CUSTOM_URL);
+ if (mCustomUrl != null) {
+ Log.d(TAG, "Using custom URL " + mCustomUrl + " for network checks");
+ }
+
final int bindPriorityFlags;
if (Boolean.valueOf(args.getString(ARG_WAIVE_BIND_PRIORITY, "false"))) {
bindPriorityFlags = Context.BIND_WAIVE_PRIORITY;
@@ -502,25 +511,23 @@
*/
private String checkNetworkAccess(boolean expectAvailable,
@Nullable final String expectedUnavailableError) throws Exception {
- final String resultData = mServiceClient.checkNetworkStatus();
- return checkForAvailabilityInResultData(resultData, expectAvailable,
+ final NetworkCheckResult checkResult = mServiceClient.checkNetworkStatus(mCustomUrl);
+ return checkForAvailabilityInNetworkCheckResult(checkResult, expectAvailable,
expectedUnavailableError);
}
- private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable,
- @Nullable final String expectedUnavailableError) {
- if (resultData == null) {
- assertNotNull("Network status from app2 is null", resultData);
- }
- // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
- final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
- assertEquals("Wrong network status: " + resultData, 5, parts.length);
- final State state = parts[0].equals("null") ? null : State.valueOf(parts[0]);
- final DetailedState detailedState = parts[1].equals("null")
- ? null : DetailedState.valueOf(parts[1]);
- final boolean connected = Boolean.valueOf(parts[2]);
- final String connectionCheckDetails = parts[3];
- final String networkInfo = parts[4];
+ private String checkForAvailabilityInNetworkCheckResult(NetworkCheckResult networkCheckResult,
+ boolean expectAvailable, @Nullable final String expectedUnavailableError) {
+ assertNotNull("NetworkCheckResult from app2 is null", networkCheckResult);
+
+ final NetworkInfo networkInfo = networkCheckResult.networkInfo;
+ assertNotNull("NetworkInfo from app2 is null", networkInfo);
+
+ final State state = networkInfo.getState();
+ final DetailedState detailedState = networkInfo.getDetailedState();
+
+ final boolean connected = networkCheckResult.connected;
+ final String connectionCheckDetails = networkCheckResult.details;
final StringBuilder errors = new StringBuilder();
final State expectedState;
@@ -926,33 +933,36 @@
if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
startForegroundService();
assertForegroundServiceNetworkAccess();
- return;
} else if (type == TYPE_COMPONENT_ACTIVTIY) {
turnScreenOn();
final CountDownLatch latch = new CountDownLatch(1);
final Intent launchIntent = getIntentForComponent(type);
final Bundle extras = new Bundle();
- final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result =
+ new AtomicReference<>();
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ extras.putString(KEY_CUSTOM_URL, mCustomUrl);
launchIntent.putExtras(extras);
mContext.startActivity(launchIntent);
if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- final int resultCode = result.get(0).first;
- final String resultData = result.get(0).second;
+ final int resultCode = result.get().first;
+ final NetworkCheckResult networkCheckResult = result.get().second;
if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
- final String error = checkForAvailabilityInResultData(
- resultData, expectAvailable, null /* expectedUnavailableError */);
+ final String error = checkForAvailabilityInNetworkCheckResult(
+ networkCheckResult, expectAvailable,
+ null /* expectedUnavailableError */);
if (error != null) {
fail("Network is not available for activity in app2 (" + mUid + "): "
+ error);
}
} else if (resultCode == INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE) {
- Log.d(TAG, resultData);
+ Log.d(TAG, networkCheckResult.details);
// App didn't come to foreground when the activity is started, so try again.
assertTopNetworkAccess(true);
} else {
- fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
+ fail("Unexpected resultCode=" + resultCode
+ + "; networkCheckResult=[" + networkCheckResult + "]");
}
} else {
fail("Timed out waiting for network availability status from app2's activity ("
@@ -960,10 +970,12 @@
}
} else if (type == TYPE_EXPEDITED_JOB) {
final Bundle extras = new Bundle();
- final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result =
+ new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ extras.putString(KEY_CUSTOM_URL, mCustomUrl);
final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT)
.setExpedited(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
@@ -973,11 +985,12 @@
RESULT_SUCCESS, mServiceClient.scheduleJob(jobInfo));
forceRunJob(TEST_APP2_PKG, TEST_JOB_ID);
if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- final int resultCode = result.get(0).first;
- final String resultData = result.get(0).second;
+ final int resultCode = result.get().first;
+ final NetworkCheckResult networkCheckResult = result.get().second;
if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
- final String error = checkForAvailabilityInResultData(
- resultData, expectAvailable, null /* expectedUnavailableError */);
+ final String error = checkForAvailabilityInNetworkCheckResult(
+ networkCheckResult, expectAvailable,
+ null /* expectedUnavailableError */);
if (error != null) {
Log.d(TAG, "Network state is unexpected, checking again. " + error);
// Right now we could end up in an unexpected state if expedited job
@@ -985,7 +998,8 @@
assertNetworkAccess(expectAvailable, false /* needScreenOn */);
}
} else {
- fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
+ fail("Unexpected resultCode=" + resultCode
+ + "; networkCheckResult=[" + networkCheckResult + "]");
}
} else {
fail("Timed out waiting for network availability status from app2's expedited job ("
@@ -1028,11 +1042,12 @@
}
private Binder getNewNetworkStateObserver(final CountDownLatch latch,
- final ArrayList<Pair<Integer, String>> result) {
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result) {
return new INetworkStateObserver.Stub() {
@Override
- public void onNetworkStateChecked(int resultCode, String resultData) {
- result.add(Pair.create(resultCode, resultData));
+ public void onNetworkStateChecked(int resultCode,
+ NetworkCheckResult networkCheckResult) {
+ result.set(Pair.create(resultCode, networkCheckResult));
latch.countDown();
}
};
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
index c1d576d..3e22a23 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
@@ -69,6 +69,7 @@
@RequiredProperties({BATTERY_SAVER_MODE})
public void testStartActivity_batterySaver() throws Exception {
setBatterySaverMode(true);
+ assertNetworkAccess(false, null);
assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver", null);
}
@@ -76,6 +77,7 @@
@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
public void testStartActivity_dataSaver() throws Exception {
setRestrictBackground(true);
+ assertNetworkAccess(false, null);
assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver", null);
}
@@ -83,6 +85,7 @@
@RequiredProperties({DOZE_MODE})
public void testStartActivity_doze() throws Exception {
setDozeMode(true);
+ assertNetworkAccess(false, null);
// TODO (235284115): We need to turn on Doze every time before starting
// the activity.
assertLaunchedActivityHasNetworkAccess("testStartActivity_doze", null);
@@ -93,6 +96,7 @@
public void testStartActivity_appStandby() throws Exception {
turnBatteryOn();
setAppIdle(true);
+ assertNetworkAccess(false, null);
// TODO (235284115): We need to put the app into app standby mode every
// time before starting the activity.
assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby", null);
@@ -104,6 +108,7 @@
assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> {
assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+ assertNetworkAccess(false, null);
});
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 980ecd5..494192f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -98,9 +98,10 @@
return mService.getCounters(receiverName, action);
}
- public String checkNetworkStatus() throws RemoteException {
+ /** Retrieves the network state as observed from the bound test app */
+ public NetworkCheckResult checkNetworkStatus(String address) throws RemoteException {
ensureServiceConnection();
- return mService.checkNetworkStatus();
+ return mService.checkNetworkStatus(address);
}
public String getRestrictBackgroundStatus() throws RemoteException {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index f81a03d..0f86d78 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -23,8 +23,8 @@
import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.ConnectivityManager.TYPE_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@@ -123,6 +123,7 @@
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.PacketBuilder;
+import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.RecorderCallback;
@@ -233,9 +234,13 @@
// The registered callbacks.
private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>();
- @Rule
+ @Rule(order = 1)
public final DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+ @Rule(order = 2)
+ public final AutoReleaseNetworkCallbackRule
+ mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule();
+
private boolean supportedHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager();
return !pm.hasSystemFeature("android.hardware.type.watch");
@@ -273,7 +278,6 @@
public void tearDown() throws Exception {
restorePrivateDnsSetting();
mRemoteSocketFactoryClient.unbind();
- mCtsNetUtils.tearDown();
Log.i(TAG, "Stopping VPN");
stopVpn();
unregisterRegisteredCallbacks();
@@ -890,9 +894,7 @@
testAndCleanup(() -> {
// Ensure both of wifi and mobile data are connected.
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- assertTrue("Wifi is not connected", (wifiNetwork != null));
- final Network cellNetwork = mCtsNetUtils.connectToCell();
- assertTrue("Mobile data is not connected", (cellNetwork != null));
+ final Network cellNetwork = mNetworkCallbackRule.requestCell();
// Store current default network.
final Network defaultNetwork = mCM.getActiveNetwork();
// Start VPN and set empty array as its underlying networks.
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
index 37dc7a0..1c45579 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -15,10 +15,16 @@
*/
package com.android.cts.net.hostside.app2;
+import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_OTHER;
+import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES;
+import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Process;
@@ -26,6 +32,12 @@
import android.util.Log;
import com.android.cts.net.hostside.INetworkStateObserver;
+import com.android.cts.net.hostside.NetworkCheckResult;
+
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
public final class Common {
@@ -48,6 +60,9 @@
static final String ACTION_SNOOZE_WARNING =
"com.android.server.net.action.SNOOZE_WARNING";
+ private static final String DEFAULT_TEST_URL =
+ "https://connectivitycheck.android.com/generate_204";
+
static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
static final String NOTIFICATION_TYPE_DELETE = "DELETE";
static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
@@ -59,10 +74,12 @@
static final String TEST_PKG = "com.android.cts.net.hostside";
static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
+ static final String KEY_CUSTOM_URL = TEST_PKG + ".custom_url";
static final int TYPE_COMPONENT_ACTIVTY = 0;
static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
static final int TYPE_COMPONENT_EXPEDITED_JOB = 2;
+ private static final int NETWORK_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(10);
static int getUid(Context context) {
final String packageName = context.getPackageName();
@@ -73,6 +90,15 @@
}
}
+ private static NetworkCheckResult createNetworkCheckResult(boolean connected, String details,
+ NetworkInfo networkInfo) {
+ final NetworkCheckResult checkResult = new NetworkCheckResult();
+ checkResult.connected = connected;
+ checkResult.details = details;
+ checkResult.networkInfo = networkInfo;
+ return checkResult;
+ }
+
private static boolean validateComponentState(Context context, int componentType,
INetworkStateObserver observer) throws RemoteException {
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
@@ -80,9 +106,9 @@
case TYPE_COMPONENT_ACTIVTY: {
final int procState = activityManager.getUidProcessState(Process.myUid());
if (procState != ActivityManager.PROCESS_STATE_TOP) {
- observer.onNetworkStateChecked(
- INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
- "Unexpected procstate: " + procState);
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ createNetworkCheckResult(false, "Unexpected procstate: " + procState,
+ null));
return false;
}
return true;
@@ -90,9 +116,9 @@
case TYPE_COMPONENT_FOREGROUND_SERVICE: {
final int procState = activityManager.getUidProcessState(Process.myUid());
if (procState != ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- observer.onNetworkStateChecked(
- INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
- "Unexpected procstate: " + procState);
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ createNetworkCheckResult(false, "Unexpected procstate: " + procState,
+ null));
return false;
}
return true;
@@ -101,16 +127,17 @@
final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
if ((capabilities
& ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) == 0) {
- observer.onNetworkStateChecked(
- INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES,
- "Unexpected capabilities: " + capabilities);
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_CAPABILITIES,
+ createNetworkCheckResult(false,
+ "Unexpected capabilities: " + capabilities, null));
return false;
}
return true;
}
default: {
- observer.onNetworkStateChecked(INetworkStateObserver.RESULT_ERROR_OTHER,
- "Unknown component type: " + componentType);
+ observer.onNetworkStateChecked(RESULT_ERROR_OTHER,
+ createNetworkCheckResult(false, "Unknown component type: " + componentType,
+ null));
return false;
}
}
@@ -131,6 +158,7 @@
final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
if (observer != null) {
+ final String customUrl = extras.getString(KEY_CUSTOM_URL);
try {
final boolean skipValidation = extras.getBoolean(KEY_SKIP_VALIDATION_CHECKS);
if (!skipValidation && !validateComponentState(context, componentType, observer)) {
@@ -143,11 +171,64 @@
try {
observer.onNetworkStateChecked(
INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
- MyBroadcastReceiver.checkNetworkStatus(context));
+ checkNetworkStatus(context, customUrl));
} catch (RemoteException e) {
Log.e(TAG, "Error occurred while notifying the observer: " + e);
}
});
}
}
+
+ /**
+ * Checks whether the network is available by attempting a connection to the given address
+ * and returns a {@link NetworkCheckResult} object containing all the relevant details for
+ * debugging. Uses a default address if the given address is {@code null}.
+ *
+ * <p>
+ * The returned object has the following fields:
+ *
+ * <ul>
+ * <li>{@code connected}: whether or not the connection was successful.
+ * <li>{@code networkInfo}: the {@link NetworkInfo} describing the current active network as
+ * visible to this app.
+ * <li>{@code details}: A human readable string giving useful information about the success or
+ * failure.
+ * </ul>
+ */
+ static NetworkCheckResult checkNetworkStatus(Context context, String customUrl) {
+ final String address = (customUrl == null) ? DEFAULT_TEST_URL : customUrl;
+
+ // The current Android DNS resolver returns an UnknownHostException whenever network access
+ // is blocked. This can get cached in the current process-local InetAddress cache. Clearing
+ // the cache before attempting a connection ensures we never report a failure due to a
+ // negative cache entry.
+ InetAddress.clearDnsCache();
+
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+
+ final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ Log.d(TAG, "Running checkNetworkStatus() on thread "
+ + Thread.currentThread().getName() + " for UID " + getUid(context)
+ + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+ boolean checkStatus = false;
+ String checkDetails = "N/A";
+ try {
+ final URL url = new URL(address);
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+ conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+ conn.setRequestMethod("GET");
+ conn.connect();
+ final int response = conn.getResponseCode();
+ checkStatus = true;
+ checkDetails = "HTTP response for " + address + ": " + response;
+ } catch (Exception e) {
+ checkStatus = false;
+ checkDetails = "Exception getting " + address + ": " + e;
+ }
+ final NetworkCheckResult result = createNetworkCheckResult(checkStatus, checkDetails,
+ networkInfo);
+ Log.d(TAG, "Offering: " + result);
+ return result;
+ }
}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 825f2c9..1fd3745 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -30,7 +30,6 @@
import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
import static com.android.cts.net.hostside.app2.Common.TAG;
-import static com.android.cts.net.hostside.app2.Common.getUid;
import android.app.Notification;
import android.app.Notification.Action;
@@ -42,15 +41,10 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.URL;
-
/**
* Receiver used to:
* <ol>
@@ -60,8 +54,6 @@
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
- private static final int NETWORK_TIMEOUT_MS = 5 * 1000;
-
private final String mName;
public MyBroadcastReceiver() {
@@ -126,82 +118,6 @@
return String.valueOf(apiStatus);
}
- private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s";
- /**
- * Checks whether the network is available and return a string which can then be send as a
- * result data for the ordered broadcast.
- *
- * <p>
- * The string has the following format:
- *
- * <p><pre><code>
- * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo
- * </code></pre>
- *
- * <p>Where:
- *
- * <ul>
- * <li>{@code NetinfoState}: enum value of {@link NetworkInfo.State}.
- * <li>{@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}.
- * <li>{@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt
- * to access an external website.
- * <li>{@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real
- * connection attempt
- * <li>{@code Netinfo}: string representation of the {@link NetworkInfo}.
- * </ul>
- *
- * For example, if the connection was established fine, the result would be something like:
- * <p><pre><code>
- * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...]
- * </code></pre>
- *
- */
- // TODO: now that it uses Binder, it counl return a Bundle with the data parts instead...
- static String checkNetworkStatus(Context context) {
- final ConnectivityManager cm =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- // TODO: connect to a hostside server instead
- final String address = "http://example.com";
- final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- Log.d(TAG, "Running checkNetworkStatus() on thread "
- + Thread.currentThread().getName() + " for UID " + getUid(context)
- + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
- boolean checkStatus = false;
- String checkDetails = "N/A";
- try {
- final URL url = new URL(address);
- final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setReadTimeout(NETWORK_TIMEOUT_MS);
- conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
- conn.setRequestMethod("GET");
- conn.setDoInput(true);
- conn.connect();
- final int response = conn.getResponseCode();
- checkStatus = true;
- checkDetails = "HTTP response for " + address + ": " + response;
- } catch (Exception e) {
- checkStatus = false;
- checkDetails = "Exception getting " + address + ": " + e;
- }
- // If the app tries to make a network connection in the foreground immediately after
- // trying to do the same when it's network access was blocked, it could receive a
- // UnknownHostException due to the cached DNS entry. So, clear the dns cache after
- // every network access for now until we have a fix on the platform side.
- InetAddress.clearDnsCache();
- Log.d(TAG, checkDetails);
- final String state, detailedState;
- if (networkInfo != null) {
- state = networkInfo.getState().name();
- detailedState = networkInfo.getDetailedState().name();
- } else {
- state = detailedState = "null";
- }
- final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState,
- Boolean.valueOf(checkStatus), checkDetails, networkInfo);
- Log.d(TAG, "Offering " + status);
- return status;
- }
-
/**
* Sends a system notification containing actions with pending intents to launch the app's
* main activitiy or service.
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
index 3ed5391..5010234 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -21,7 +21,6 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_SNOOZE_WARNING;
import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
import static com.android.cts.net.hostside.app2.Common.TAG;
-import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_EXPORTED;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -41,6 +40,7 @@
import com.android.cts.net.hostside.IMyService;
import com.android.cts.net.hostside.INetworkCallback;
+import com.android.cts.net.hostside.NetworkCheckResult;
import com.android.modules.utils.build.SdkLevel;
/**
@@ -56,9 +56,7 @@
// TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
- private IMyService.Stub mBinder =
- new IMyService.Stub() {
-
+ private IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public void registerBroadcastReceiver() {
if (mReceiver != null) {
@@ -83,8 +81,8 @@
}
@Override
- public String checkNetworkStatus() {
- return MyBroadcastReceiver.checkNetworkStatus(getApplicationContext());
+ public NetworkCheckResult checkNetworkStatus(String customUrl) {
+ return Common.checkNetworkStatus(getApplicationContext(), customUrl);
}
@Override
@@ -94,7 +92,7 @@
@Override
public void sendNotification(int notificationId, String notificationType) {
- MyBroadcastReceiver .sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
+ MyBroadcastReceiver.sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
notificationId, notificationType);
}
@@ -170,7 +168,7 @@
.getSystemService(JobScheduler.class);
return jobScheduler.schedule(jobInfo);
}
- };
+ };
@Override
public IBinder onBind(Intent intent) {
diff --git a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
index 472e347..911b129 100644
--- a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
+++ b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
@@ -18,4 +18,5 @@
public interface InstrumentationArguments {
String ARG_WAIVE_BIND_PRIORITY = "waive_bind_priority";
+ String ARG_CONNECTION_CHECK_CUSTOM_URL = "connection_check_custom_url";
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
index 880e826..fff716d 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -47,29 +47,29 @@
@Test
public void testStartActivity_batterySaver() throws Exception {
- runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
}
@Test
public void testStartActivity_dataSaver() throws Exception {
- runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
}
@FlakyTest(bugId = 231440256)
@Test
public void testStartActivity_doze() throws Exception {
- runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
}
@Test
public void testStartActivity_appStandby() throws Exception {
- runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
}
// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
@Test
public void testStartActivity_default() throws Exception {
- runDeviceTestsWithArgs(TEST_PKG, TEST_CLASS, "testStartActivity_default",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_default",
Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
}
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
index 0d01fc1..faabbef 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
@@ -46,12 +46,12 @@
}
private void runMeteredTest(String methodName) throws DeviceNotAvailableException {
- runDeviceTestsWithArgs(TEST_PKG, METERED_TEST_CLASS, methodName,
+ runDeviceTestsWithCustomOptions(TEST_PKG, METERED_TEST_CLASS, methodName,
Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
}
private void runNonMeteredTest(String methodName) throws DeviceNotAvailableException {
- runDeviceTestsWithArgs(TEST_PKG, NON_METERED_TEST_CLASS, methodName,
+ runDeviceTestsWithCustomOptions(TEST_PKG, NON_METERED_TEST_CLASS, methodName,
Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
index 361f7c7..c4bcdfd 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -41,20 +41,20 @@
@Test
public void testOnBlockedStatusChanged_dataSaver() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
}
@Test
public void testOnBlockedStatusChanged_powerSaver() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
}
// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
@Test
public void testOnBlockedStatusChanged_default() throws Exception {
- runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkCallbackTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkCallbackTest",
"testOnBlockedStatusChanged_default", Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
}
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
index e97db58..4730b14 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
@@ -38,48 +38,48 @@
@Test
public void testIsUidNetworkingBlocked_withUidNotBlocked() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest",
"testIsUidNetworkingBlocked_withUidNotBlocked");
}
@Test
public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidNetworkingBlocked_withSystemUid");
}
@Test
public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest",
"testIsUidNetworkingBlocked_withDataSaverMode");
}
@Test
public void testIsUidNetworkingBlocked_withRestrictedNetworkingMode() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest",
"testIsUidNetworkingBlocked_withRestrictedNetworkingMode");
}
@Test
public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest",
"testIsUidNetworkingBlocked_withPowerSaverMode");
}
@Test
public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
- runDeviceTests(TEST_PKG,
+ runDeviceTestsWithCustomOptions(TEST_PKG,
TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidRestrictedOnMeteredNetworks");
}
// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
@Test
public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
- runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest",
"testIsUidNetworkingBlocked_whenInBackground",
Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index ca95ed6..d7dfa80 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -16,12 +16,15 @@
package com.android.cts.net;
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import com.android.ddmlib.Log;
import com.android.modules.utils.build.testing.DeviceSdkLevel;
+import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.targetprep.BuildError;
@@ -48,6 +51,10 @@
protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
+ @Option(name = "custom-url", importance = Option.Importance.IF_UNSET,
+ description = "A custom url to use for testing network connections")
+ protected String mCustomUrl;
+
@BeforeClassWithInfo
public static void setUpOnceBase(TestInformation testInfo) throws Exception {
DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(testInfo.getDevice());
@@ -149,13 +156,31 @@
+ packageName + ", u=" + currentUser);
}
- protected boolean runDeviceTestsWithArgs(String packageName, String className,
- String methodName, Map<String, String> args) throws DeviceNotAvailableException {
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className)
+ throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName) throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, methodName, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName, Map<String, String> testArgs) throws DeviceNotAvailableException {
final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName)
.setTestClassName(className)
.setTestMethodName(methodName);
- for (Map.Entry<String, String> arg : args.entrySet()) {
- deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
+
+ // Currently there is only one custom option that the test exposes.
+ if (mCustomUrl != null) {
+ deviceTestRunOptions.addInstrumentationArg(ARG_CONNECTION_CHECK_CUSTOM_URL, mCustomUrl);
+ }
+ // Pass over any test specific arguments.
+ if (testArgs != null) {
+ for (Map.Entry<String, String> arg : testArgs.entrySet()) {
+ deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
+ }
}
return runDeviceTests(deviceTestRunOptions);
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 9c3751d..7b9d3b5 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -46,7 +46,7 @@
@SecurityTest
@Test
public void testDataWarningReceiver() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataWarningReceiverTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataWarningReceiverTest",
"testSnoozeWarningNotReceived");
}
@@ -56,25 +56,25 @@
@Test
public void testDataSaverMode_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testGetRestrictBackgroundStatus_disabled");
}
@Test
public void testDataSaverMode_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testGetRestrictBackgroundStatus_whitelisted");
}
@Test
public void testDataSaverMode_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testGetRestrictBackgroundStatus_enabled");
}
@Test
public void testDataSaverMode_blacklisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testGetRestrictBackgroundStatus_blacklisted");
}
@@ -97,13 +97,13 @@
@Test
public void testDataSaverMode_requiredWhitelistedPackages() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
}
@Test
public void testDataSaverMode_broadcastNotSentOnUnsupportedDevices() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
"testBroadcastNotSentOnUnsupportedDevices");
}
@@ -113,19 +113,19 @@
@Test
public void testBatterySaverModeMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testBatterySaverModeMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testBatterySaverModeMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@@ -149,19 +149,19 @@
@Test
public void testBatterySaverModeNonMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testBatterySaverModeNonMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testBatterySaverModeNonMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@@ -171,31 +171,31 @@
@Test
public void testAppIdleMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testAppIdleMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testAppIdleMetered_tempWhitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_tempWhitelisted");
}
@Test
public void testAppIdleMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@Test
public void testAppIdleMetered_idleWhitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testAppIdleNetworkAccess_idleWhitelisted");
}
@@ -206,51 +206,51 @@
@Test
public void testAppIdleNonMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testAppIdleNonMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_tempWhitelisted");
}
@Test
public void testAppIdleNonMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@Test
public void testAppIdleNonMetered_idleWhitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testAppIdleNetworkAccess_idleWhitelisted");
}
@Test
public void testAppIdleNonMetered_whenCharging() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testAppIdleNetworkAccess_whenCharging");
}
@Test
public void testAppIdleMetered_whenCharging() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testAppIdleNetworkAccess_whenCharging");
}
@Test
public void testAppIdle_toast() throws Exception {
// Check that showing a toast doesn't bring an app out of standby
- runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testAppIdle_toast");
}
@@ -260,25 +260,25 @@
@Test
public void testDozeModeMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testDozeModeMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testDozeModeMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@Test
public void testDozeModeMetered_enabledButWhitelistedOnNotificationAction() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
"testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
}
@@ -289,26 +289,26 @@
@Test
public void testDozeModeNonMetered_disabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
"testBackgroundNetworkAccess_disabled");
}
@Test
public void testDozeModeNonMetered_whitelisted() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
}
@Test
public void testDozeModeNonMetered_enabled() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
"testBackgroundNetworkAccess_enabled");
}
@Test
public void testDozeModeNonMetered_enabledButWhitelistedOnNotificationAction()
throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
"testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
}
@@ -318,55 +318,55 @@
@Test
public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testDataAndBatterySaverModes_meteredNetwork");
}
@Test
public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testDataAndBatterySaverModes_nonMeteredNetwork");
}
@Test
public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testDozeAndBatterySaverMode_powerSaveWhitelists");
}
@Test
public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testDozeAndAppIdle_powerSaveWhitelists");
}
@Test
public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testAppIdleAndDoze_tempPowerSaveWhitelists");
}
@Test
public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
}
@Test
public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testDozeAndAppIdle_appIdleWhitelist");
}
@Test
public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists");
}
@Test
public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
"testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists");
}
@@ -376,13 +376,13 @@
@Test
public void testNetworkAccess_restrictedMode() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
"testNetworkAccess");
}
@Test
public void testNetworkAccess_restrictedMode_withBatterySaver() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
"testNetworkAccess_withBatterySaver");
}
@@ -392,12 +392,12 @@
@Test
public void testMeteredNetworkAccess_expeditedJob() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
}
@Test
public void testNonMeteredNetworkAccess_expeditedJob() throws Exception {
- runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
}
/*******************
diff --git a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
index 115210b..c883b78 100644
--- a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
@@ -35,6 +35,7 @@
import android.net.wifi.WifiSsid
import androidx.test.platform.app.InstrumentationRegistry
import com.android.testutils.ConnectUtil
+import com.android.testutils.NetworkCallbackHelper
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.TestableNetworkCallback
@@ -48,9 +49,14 @@
private val cm = context.getSystemService(ConnectivityManager::class.java)!!
private val pm = context.packageManager
private val ctsNetUtils = CtsNetUtils(context)
+ private val cbHelper = NetworkCallbackHelper()
private val ctsTetheringUtils = CtsTetheringUtils(context)
private var oldSoftApConfig: SoftApConfiguration? = null
+ override fun shutdown() {
+ cbHelper.unregisterAll()
+ }
+
@Rpc(description = "Check whether the device has wifi feature.")
fun hasWifiFeature() = pm.hasSystemFeature(FEATURE_WIFI)
@@ -65,13 +71,13 @@
@Rpc(description = "Request cellular connection and ensure it is the default network.")
fun requestCellularAndEnsureDefault() {
ctsNetUtils.disableWifi()
- val network = ctsNetUtils.connectToCell()
+ val network = cbHelper.requestCell()
ctsNetUtils.expectNetworkIsSystemDefault(network)
}
@Rpc(description = "Unrequest cellular connection.")
fun unrequestCellular() {
- ctsNetUtils.disconnectFromCell()
+ cbHelper.unrequestCell()
}
@Rpc(description = "Ensure any wifi is connected and is the default network.")
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 3ecbdc6..3be44f7 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -13,49 +13,131 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+// ktlint does not allow annotating function argument literals inline. Disable the specific rule
+// since this negatively affects readability.
+@file:Suppress("ktlint:standard:comment-wrapping")
+
package android.net.cts
+import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.content.pm.PackageManager.FEATURE_WIFI
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.NetworkRequest
+import android.net.apf.ApfCapabilities
import android.os.Build
+import android.os.PowerManager
import android.platform.test.annotations.AppModeFull
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
import android.system.OsConstants
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.compatibility.common.util.PropertyUtil.isVendorApiLevelNewerThan
-import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.PropertyUtil.getVsrApiLevel
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.internal.util.HexDump
import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.NetworkStackModuleTest
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
+import com.android.testutils.SkipPresubmit
import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.runAsShell
import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertEquals
+import com.google.common.truth.Truth.assertWithMessage
+import com.google.common.truth.TruthJUnit.assume
+import java.lang.Thread
+import kotlin.random.Random
import kotlin.test.assertNotNull
import org.junit.After
-import org.junit.Assume.assumeTrue
import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+private const val TAG = "ApfIntegrationTest"
private const val TIMEOUT_MS = 2000L
+private const val APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version"
+private const val POLLING_INTERVAL_MS: Int = 100
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
@RunWith(DevSdkIgnoreRunner::class)
@NetworkStackModuleTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
class ApfIntegrationTest {
+ companion object {
+ @BeforeClass
+ @JvmStatic
+ @Suppress("ktlint:standard:no-multi-spaces")
+ fun setupOnce() {
+ // TODO: check that there is no active wifi network. Otherwise, ApfFilter has already been
+ // created.
+ // APF adb cmds are only implemented in ApfFilter.java. Enable experiment to prevent
+ // LegacyApfFilter.java from being used.
+ runAsShell(WRITE_DEVICE_CONFIG) {
+ DeviceConfig.setProperty(
+ NAMESPACE_CONNECTIVITY,
+ APF_NEW_RA_FILTER_VERSION,
+ "1", // value => force enabled
+ false // makeDefault
+ )
+ }
+ }
+ }
+
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule()
+
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val pm by lazy { context.packageManager }
+ private val powerManager by lazy { context.getSystemService(PowerManager::class.java)!! }
+ private val wakeLock by lazy { powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG) }
private lateinit var ifname: String
private lateinit var networkCallback: TestableNetworkCallback
+ private lateinit var caps: ApfCapabilities
+
+ fun getApfCapabilities(): ApfCapabilities {
+ val caps = runShellCommand("cmd network_stack apf $ifname capabilities").trim()
+ if (caps.isEmpty()) {
+ return ApfCapabilities(0, 0, 0)
+ }
+ val (version, maxLen, packetFormat) = caps.split(",").map { it.toInt() }
+ return ApfCapabilities(version, maxLen, packetFormat)
+ }
+
+ fun pollingCheck(condition: () -> Boolean, timeout_ms: Int): Boolean {
+ var polling_time = 0
+ do {
+ Thread.sleep(POLLING_INTERVAL_MS.toLong())
+ polling_time += POLLING_INTERVAL_MS
+ if (condition()) return true
+ } while (polling_time < timeout_ms)
+ return false
+ }
+
+ fun turnScreenOff() {
+ if (!wakeLock.isHeld()) wakeLock.acquire()
+ runShellCommandOrThrow("input keyevent KEYCODE_SLEEP")
+ val result = pollingCheck({ !powerManager.isInteractive() }, timeout_ms = 2000)
+ assertThat(result).isTrue()
+ }
+
+ fun turnScreenOn() {
+ if (wakeLock.isHeld()) wakeLock.release()
+ runShellCommandOrThrow("input keyevent KEYCODE_WAKEUP")
+ val result = pollingCheck({ powerManager.isInteractive() }, timeout_ms = 2000)
+ assertThat(result).isTrue()
+ }
@Before
fun setUp() {
- assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
- assumeTrue(isVendorApiLevelNewerThan(Build.VERSION_CODES.TIRAMISU))
+ assume().that(pm.hasSystemFeature(FEATURE_WIFI)).isTrue()
+ // APF must run when the screen is off and the device is not interactive.
+ // TODO: consider running some of the tests with screen on (capabilities, read / write).
+ turnScreenOff()
+
networkCallback = TestableNetworkCallback()
cm.requestNetwork(
NetworkRequest.Builder()
@@ -68,24 +150,90 @@
ifname = assertNotNull(it.lp.interfaceName)
true
}
+ // It's possible the device does not support APF, in which case this command will not be
+ // successful. Ignore the error as testApfCapabilities() already asserts APF support on the
+ // respective VSR releases and all other tests are based on the capabilities indicated.
+ runShellCommand("cmd network_stack apf $ifname pause")
+ caps = getApfCapabilities()
}
@After
fun tearDown() {
+ if (::ifname.isInitialized) {
+ runShellCommand("cmd network_stack apf $ifname resume")
+ }
if (::networkCallback.isInitialized) {
cm.unregisterNetworkCallback(networkCallback)
}
+ turnScreenOn()
}
@Test
- fun testGetApfCapabilities() {
- val caps = SystemUtil.runShellCommand("cmd network_stack apf $ifname capabilities").trim()
- val (version, maxLen, packetFormat) = caps.split(",").map { it.toInt() }
- assertEquals(4, version)
- assertThat(maxLen).isAtLeast(1024)
- if (isVendorApiLevelNewerThan(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) {
- assertThat(maxLen).isAtLeast(2000)
+ fun testApfCapabilities() {
+ // APF became mandatory in Android 14 VSR.
+ assume().that(getVsrApiLevel()).isAtLeast(34)
+
+ // ApfFilter does not support anything but ARPHRD_ETHER.
+ assertThat(caps.apfPacketFormat).isEqualTo(OsConstants.ARPHRD_ETHER)
+
+ // DEVICEs launching with Android 14 with CHIPSETs that set ro.board.first_api_level to 34:
+ // - [GMS-VSR-5.3.12-003] MUST return 4 or higher as the APF version number from calls to
+ // the getApfPacketFilterCapabilities HAL method.
+ // - [GMS-VSR-5.3.12-004] MUST indicate at least 1024 bytes of usable memory from calls to
+ // the getApfPacketFilterCapabilities HAL method.
+ // TODO: check whether above text should be changed "34 or higher"
+ // This should assert apfVersionSupported >= 4 as per the VSR requirements, but there are
+ // currently no tests for APFv6 and there cannot be a valid implementation as the
+ // interpreter has yet to be finalized.
+ assertThat(caps.apfVersionSupported).isEqualTo(4)
+ assertThat(caps.maximumApfProgramSize).isAtLeast(1024)
+
+ // DEVICEs launching with Android 15 (AOSP experimental) or higher with CHIPSETs that set
+ // ro.board.first_api_level or ro.board.api_level to 202404 or higher:
+ // - [GMS-VSR-5.3.12-009] MUST indicate at least 2000 bytes of usable memory from calls to
+ // the getApfPacketFilterCapabilities HAL method.
+ if (getVsrApiLevel() >= 202404) {
+ assertThat(caps.maximumApfProgramSize).isAtLeast(2000)
}
- assertEquals(OsConstants.ARPHRD_ETHER, packetFormat)
+ }
+
+ // APF is backwards compatible, i.e. a v6 interpreter supports both v2 and v4 functionality.
+ fun assumeApfVersionSupportAtLeast(version: Int) {
+ assume().that(caps.apfVersionSupported).isAtLeast(version)
+ }
+
+ fun installProgram(bytes: ByteArray) {
+ val prog = HexDump.toHexString(bytes, 0 /* offset */, bytes.size, false /* upperCase */)
+ val result = runShellCommandOrThrow("cmd network_stack apf $ifname install $prog").trim()
+ // runShellCommandOrThrow only throws on S+.
+ assertThat(result).isEqualTo("success")
+ }
+
+ fun readProgram(): ByteArray {
+ val progHexString = runShellCommandOrThrow("cmd network_stack apf $ifname read").trim()
+ // runShellCommandOrThrow only throws on S+.
+ assertThat(progHexString).isNotEmpty()
+ return HexDump.hexStringToByteArray(progHexString)
+ }
+
+ @SkipPresubmit(reason = "This test takes longer than 1 minute, do not run it on presubmit.")
+ // APF integration is mostly broken before V, only run the full read / write test on V+.
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testReadWriteProgram() {
+ assumeApfVersionSupportAtLeast(4)
+
+ // Only test down to 2 bytes. The first byte always stays PASS.
+ val program = ByteArray(caps.maximumApfProgramSize)
+ for (i in caps.maximumApfProgramSize downTo 2) {
+ // Randomize bytes in range [1, i). And install first [0, i) bytes of program.
+ // Note that only the very first instruction (PASS) is valid APF bytecode.
+ Random.nextBytes(program, 1 /* fromIndex */, i /* toIndex */)
+ installProgram(program.sliceArray(0..<i))
+
+ // Compare entire memory region.
+ val readResult = readProgram()
+ assertWithMessage("read/write $i byte prog failed").that(readResult).isEqualTo(program)
+ }
}
}
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index 3e5d0ba..16a7b73 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -48,6 +48,7 @@
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.DevSdkIgnoreRule;
import org.junit.Before;
@@ -67,7 +68,10 @@
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) // BatteryStatsManager did not exist on Q
public class BatteryStatsManagerTest{
- @Rule
+ @Rule(order = 1)
+ public final AutoReleaseNetworkCallbackRule
+ networkCallbackRule = new AutoReleaseNetworkCallbackRule();
+ @Rule(order = 2)
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
private static final String TAG = BatteryStatsManagerTest.class.getSimpleName();
private static final String TEST_URL = "https://connectivitycheck.gstatic.com/generate_204";
@@ -145,7 +149,7 @@
return;
}
- final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final Network cellNetwork = networkCallbackRule.requestCell();
final URL url = new URL(TEST_URL);
// Get cellular battery stats
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 99222dd..07e2024 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -47,6 +47,7 @@
import com.android.modules.utils.build.SdkLevel.isAtLeastR
import com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTPS_URL
import com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL
+import com.android.testutils.AutoReleaseNetworkCallbackRule
import com.android.testutils.DeviceConfigRule
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.SkipMainlinePresubmit
@@ -101,9 +102,12 @@
private val server = TestHttpServer("localhost")
- @get:Rule
+ @get:Rule(order = 1)
val deviceConfigRule = DeviceConfigRule(retryCountBeforeSIfConfigChanged = 5)
+ @get:Rule(order = 2)
+ val networkCallbackRule = AutoReleaseNetworkCallbackRule()
+
companion object {
@JvmStatic @BeforeClass
fun setUpClass() {
@@ -144,15 +148,15 @@
assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
assumeFalse(pm.hasSystemFeature(FEATURE_WATCH))
utils.ensureWifiConnected()
- val cellNetwork = utils.connectToCell()
+ val cellNetwork = networkCallbackRule.requestCell()
// Verify cell network is validated
val cellReq = NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.build()
- val cellCb = TestableNetworkCallback(timeoutMs = TEST_TIMEOUT_MS)
- cm.registerNetworkCallback(cellReq, cellCb)
+ val cellCb = networkCallbackRule.registerNetworkCallback(cellReq,
+ TestableNetworkCallback(timeoutMs = TEST_TIMEOUT_MS))
val cb = cellCb.poll { it.network == cellNetwork &&
it is CapabilitiesChanged && it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
}
@@ -213,8 +217,6 @@
} finally {
cm.unregisterNetworkCallback(wifiCb)
server.stop()
- // disconnectFromCell should be called after connectToCell
- utils.disconnectFromCell()
}
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index cdf8340..c0f1080 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -71,7 +71,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -81,7 +83,6 @@
import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
import static android.net.cts.util.CtsNetUtils.TEST_HOST;
-import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.INVALID_UID;
@@ -191,6 +192,7 @@
import com.android.networkstack.apishim.ConstantsShim;
import com.android.networkstack.apishim.NetworkInformationShimImpl;
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
+import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.CompatUtil;
import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
@@ -259,10 +261,14 @@
@RunWith(AndroidJUnit4.class)
public class ConnectivityManagerTest {
- @Rule
+ @Rule(order = 1)
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
- @Rule
+ @Rule(order = 2)
+ public final AutoReleaseNetworkCallbackRule
+ networkCallbackRule = new AutoReleaseNetworkCallbackRule();
+
+ @Rule(order = 3)
public final DeviceConfigRule mTestValidationConfigRule = new DeviceConfigRule(
5 /* retryCountBeforeSIfConfigChanged */);
@@ -328,8 +334,6 @@
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
- // The registered callbacks.
- private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>();
// Used for cleanup purposes.
private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -411,11 +415,6 @@
@After
public void tearDown() throws Exception {
- // Release any NetworkRequests filed to connect mobile data.
- if (mCtsNetUtils.cellConnectAttempted()) {
- mCtsNetUtils.disconnectFromCell();
- }
-
if (TestUtils.shouldTestSApis()) {
runWithShellPermissionIdentity(
() -> mCmShim.setRequireVpnForUids(false, mVpnRequiredUidRanges),
@@ -425,15 +424,12 @@
// All tests in this class require a working Internet connection as they start. Make
// sure there is still one as they end that's ready to use for the next test to use.
mTestValidationConfigRule.runAfterNextCleanup(() -> {
- final TestNetworkCallback callback = new TestNetworkCallback();
- registerDefaultNetworkCallback(callback);
- try {
- assertNotNull("Couldn't restore Internet connectivity",
- callback.waitForAvailable());
- } finally {
- // Unregister all registered callbacks.
- unregisterRegisteredCallbacks();
- }
+ // mTestValidationConfigRule has higher order than networkCallbackRule, so
+ // networkCallbackRule is the outer rule and will be cleaned up after this method.
+ final TestableNetworkCallback callback =
+ networkCallbackRule.registerDefaultNetworkCallback();
+ assertNotNull("Couldn't restore Internet connectivity",
+ callback.eventuallyExpect(CallbackEntry.AVAILABLE));
});
}
@@ -555,7 +551,7 @@
throws InterruptedException {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
// Make sure cell is active to retrieve IMSI for verification in later step.
- final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final Network cellNetwork = networkCallbackRule.requestCell();
final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
assertFalse(TextUtils.isEmpty(subscriberId));
@@ -853,7 +849,7 @@
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- Network cellNetwork = mCtsNetUtils.connectToCell();
+ Network cellNetwork = networkCallbackRule.requestCell();
// This server returns the requestor's IP address as the response body.
URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
String wifiAddressString = httpGet(wifiNetwork, url);
@@ -993,10 +989,10 @@
// default network.
return new NetworkRequest.Builder()
.clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addCapability(NET_CAPABILITY_INTERNET)
.build();
}
@@ -1027,10 +1023,10 @@
final String invalidPrivateDnsServer = "invalidhostname.example.com";
final String goodPrivateDnsServer = "dns.google";
mCtsNetUtils.storePrivateDnsSetting();
- final TestableNetworkCallback cb = new TestableNetworkCallback();
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET).build();
- registerNetworkCallback(networkRequest, cb);
+ final TestableNetworkCallback cb =
+ networkCallbackRule.registerNetworkCallback(networkRequest);
final Network networkForPrivateDns = mCm.getActiveNetwork();
try {
// Verifying the good private DNS sever
@@ -1068,24 +1064,27 @@
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
// We will register for a WIFI network being available or lost.
- final TestNetworkCallback callback = new TestNetworkCallback();
- registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ final TestableNetworkCallback callback = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
- final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
- registerDefaultNetworkCallback(defaultTrackingCallback);
+ final TestableNetworkCallback defaultTrackingCallback =
+ networkCallbackRule.registerDefaultNetworkCallback();
- final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
- final TestNetworkCallback perUidCallback = new TestNetworkCallback();
- final TestNetworkCallback bestMatchingCallback = new TestNetworkCallback();
+ final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback perUidCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback bestMatchingCallback = new TestableNetworkCallback();
final Handler h = new Handler(Looper.getMainLooper());
if (TestUtils.shouldTestSApis()) {
assertThrows(SecurityException.class, () ->
- registerSystemDefaultNetworkCallback(systemDefaultCallback, h));
+ networkCallbackRule.registerSystemDefaultNetworkCallback(
+ systemDefaultCallback, h));
runWithShellPermissionIdentity(() -> {
- registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
- registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
+ networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
+ networkCallbackRule.registerDefaultNetworkCallbackForUid(Process.myUid(),
+ perUidCallback, h);
}, NETWORK_SETTINGS);
- registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h);
+ networkCallbackRule.registerBestMatchingNetworkCallback(
+ makeDefaultRequest(), bestMatchingCallback, h);
}
Network wifiNetwork = null;
@@ -1094,24 +1093,22 @@
// Now we should expect to get a network callback about availability of the wifi
// network even if it was already connected as a state-based action when the callback
// is registered.
- wifiNetwork = callback.waitForAvailable();
+ wifiNetwork = callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
wifiNetwork);
- final Network defaultNetwork = defaultTrackingCallback.waitForAvailable();
+ final Network defaultNetwork = defaultTrackingCallback.eventuallyExpect(
+ CallbackEntry.AVAILABLE).getNetwork();
assertNotNull("Did not receive onAvailable on default network callback",
defaultNetwork);
if (TestUtils.shouldTestSApis()) {
- assertNotNull("Did not receive onAvailable on system default network callback",
- systemDefaultCallback.waitForAvailable());
- final Network perUidNetwork = perUidCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on per-UID default network callback",
- perUidNetwork);
+ systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE);
+ final Network perUidNetwork = perUidCallback.eventuallyExpect(CallbackEntry.AVAILABLE)
+ .getNetwork();
assertEquals(defaultNetwork, perUidNetwork);
- final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on best matching network callback",
- bestMatchingNetwork);
+ final Network bestMatchingNetwork = bestMatchingCallback.eventuallyExpect(
+ CallbackEntry.AVAILABLE).getNetwork();
assertEquals(defaultNetwork, bestMatchingNetwork);
}
}
@@ -1123,8 +1120,8 @@
final Handler h = new Handler(Looper.getMainLooper());
// Verify registerSystemDefaultNetworkCallback can be accessed via
// CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
- runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(new TestNetworkCallback(), h),
+ runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
@@ -1294,15 +1291,14 @@
*/
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
@Test
- public void testRequestNetworkCallback() throws Exception {
- final TestNetworkCallback callback = new TestNetworkCallback();
- requestNetwork(new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), callback);
+ public void testRequestNetworkCallback() {
+ final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder().addCapability(
+ NET_CAPABILITY_INTERNET)
+ .build());
// Wait to get callback for availability of internet
- Network internetNetwork = callback.waitForAvailable();
- assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET", internetNetwork);
+ callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
}
/**
@@ -1320,16 +1316,13 @@
}
}
- final TestNetworkCallback callback = new TestNetworkCallback();
- requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
- callback, 100);
+ final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
+ 100 /* timeoutMs */);
try {
// Wait to get callback for unavailability of requested network
- assertTrue("Did not receive NetworkCallback#onUnavailable",
- callback.waitForUnavailable());
- } catch (InterruptedException e) {
- fail("NetworkCallback wait was interrupted.");
+ callback.eventuallyExpect(CallbackEntry.UNAVAILABLE, 2_000 /* timeoutMs */);
} finally {
if (previousWifiEnabledState) {
mCtsNetUtils.connectToWifi();
@@ -1416,40 +1409,48 @@
final boolean useSystemDefault)
throws Exception {
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
- final NetworkCallback networkCallback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- if (!nc.hasTransport(targetTransportType)) return;
- final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
- final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
- if (metered == requestedMeteredness && (!waitForValidation || validated)) {
- networkFuture.complete(network);
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not, it'll wait for the setting to change.
+ final TestableNetworkCallback networkCallback;
+ if (useSystemDefault) {
+ networkCallback = runWithShellPermissionIdentity(() -> {
+ if (isAtLeastS()) {
+ return networkCallbackRule.registerSystemDefaultNetworkCallback(
+ new Handler(Looper.getMainLooper()));
+ } else {
+ // registerSystemDefaultNetworkCallback is only supported on S+.
+ return networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build(),
+ new TestableNetworkCallback(),
+ new Handler(Looper.getMainLooper()));
}
- }
- };
-
- try {
- // Registering a callback here guarantees onCapabilitiesChanged is called immediately
- // with the current setting. Therefore, if the setting has already been changed,
- // this method will return right away, and if not, it'll wait for the setting to change.
- if (useSystemDefault) {
- runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(networkCallback,
- new Handler(Looper.getMainLooper())),
- NETWORK_SETTINGS);
- } else {
- registerDefaultNetworkCallback(networkCallback);
- }
-
- // Changing meteredness on wifi involves reconnecting, which can take several seconds
- // (involves re-associating, DHCP...).
- return networkFuture.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- throw new AssertionError("Timed out waiting for active network metered status to "
- + "change to " + requestedMeteredness + " ; network = "
- + mCm.getActiveNetwork(), e);
+ },
+ NETWORK_SETTINGS);
+ } else {
+ networkCallback = networkCallbackRule.registerDefaultNetworkCallback();
}
+
+ return networkCallback.eventuallyExpect(
+ CallbackEntry.NETWORK_CAPS_UPDATED,
+ // Changing meteredness on wifi involves reconnecting, which can take several
+ // seconds (involves re-associating, DHCP...).
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ cb -> {
+ final NetworkCapabilities nc = cb.getCaps();
+ if (!nc.hasTransport(targetTransportType)) return false;
+
+ final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ return metered == requestedMeteredness && (!waitForValidation || validated);
+ }).getNetwork();
}
private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
@@ -2025,7 +2026,7 @@
return;
}
- final Network network = mCtsNetUtils.connectToCell();
+ final Network network = networkCallbackRule.requestCell();
final int supported = getSupportedKeepalivesForNet(network);
final InetAddress srcAddr = getFirstV4Address(network);
assumeTrue("This test requires native IPv4", srcAddr != null);
@@ -2091,15 +2092,15 @@
}
private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
- final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.requestNetwork(testRequest),
CONNECTIVITY_USE_RESTRICTED_NETWORKS,
// CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
// have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
@@ -2115,7 +2116,7 @@
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
@@ -2200,8 +2201,7 @@
registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
}
if (supportTelephony) {
- // connectToCell needs to be followed by disconnectFromCell, which is called in tearDown
- mCtsNetUtils.connectToCell();
+ networkCallbackRule.requestCell();
registerCallbackAndWaitForAvailable(makeCellNetworkRequest(), telephonyCb);
}
@@ -2239,7 +2239,7 @@
private void registerCallbackAndWaitForAvailable(@NonNull final NetworkRequest request,
@NonNull final TestableNetworkCallback cb) {
- registerNetworkCallback(request, cb);
+ networkCallbackRule.registerNetworkCallback(request, cb);
waitForAvailable(cb);
}
@@ -2347,21 +2347,13 @@
private void verifySsidFromCallbackNetworkCapabilities(@NonNull String ssid, boolean hasSsid)
throws Exception {
- final CompletableFuture<NetworkCapabilities> foundNc = new CompletableFuture();
- final NetworkCallback callback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- foundNc.complete(nc);
- }
- };
-
- registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ final TestableNetworkCallback callback =
+ networkCallbackRule.registerNetworkCallback(makeWifiNetworkRequest());
// Registering a callback here guarantees onCapabilitiesChanged is called immediately
// because WiFi network should be connected.
- final NetworkCapabilities nc =
- foundNc.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ final NetworkCapabilities nc = callback.eventuallyExpect(
+ CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS).getCaps();
// Verify if ssid is contained in the NetworkCapabilities received from callback.
- assertNotNull("NetworkCapabilities of the network is null", nc);
assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
}
@@ -2390,8 +2382,8 @@
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_TEST)
// Test networks do not have NOT_VPN or TRUSTED capabilities by default
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
testNetworkInterface.getInterfaceName()))
.build();
@@ -2400,14 +2392,15 @@
final TestableNetworkCallback callback = new TestableNetworkCallback();
final Handler handler = new Handler(Looper.getMainLooper());
assertThrows(SecurityException.class,
- () -> requestBackgroundNetwork(testRequest, callback, handler));
+ () -> networkCallbackRule.requestBackgroundNetwork(testRequest, callback, handler));
Network testNetwork = null;
try {
// Request background test network via Shell identity which has NETWORK_SETTINGS
// permission granted.
runWithShellPermissionIdentity(
- () -> requestBackgroundNetwork(testRequest, callback, handler),
+ () -> networkCallbackRule.requestBackgroundNetwork(
+ testRequest, callback, handler),
new String[] { android.Manifest.permission.NETWORK_SETTINGS });
// Register the test network agent which has no foreground request associated to it.
@@ -2500,9 +2493,10 @@
final int otherUid = UserHandle.getUid(5, Process.FIRST_APPLICATION_UID);
final Handler handler = new Handler(Looper.getMainLooper());
- registerDefaultNetworkCallback(myUidCallback, handler);
- runWithShellPermissionIdentity(() -> registerDefaultNetworkCallbackForUid(
- otherUid, otherUidCallback, handler), NETWORK_SETTINGS);
+ networkCallbackRule.registerDefaultNetworkCallback(myUidCallback, handler);
+ runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerDefaultNetworkCallbackForUid(
+ otherUid, otherUidCallback, handler), NETWORK_SETTINGS);
final Network defaultNetwork = myUidCallback.expect(CallbackEntry.AVAILABLE).getNetwork();
final List<DetailedBlockedStatusCallback> allCallbacks =
@@ -2558,14 +2552,14 @@
assertNotNull(info);
assertEquals(DetailedState.CONNECTED, info.getDetailedState());
- final TestableNetworkCallback callback = new TestableNetworkCallback();
+ final TestableNetworkCallback callback;
try {
mCmShim.setLegacyLockdownVpnEnabled(true);
// setLegacyLockdownVpnEnabled is asynchronous and only takes effect when the
// ConnectivityService handler thread processes it. Ensure it has taken effect by doing
// something that blocks until the handler thread is idle.
- registerDefaultNetworkCallback(callback);
+ callback = networkCallbackRule.registerDefaultNetworkCallback();
waitForAvailable(callback);
// Test one of the effects of setLegacyLockdownVpnEnabled: the fact that any NetworkInfo
@@ -2832,9 +2826,9 @@
private void registerTestOemNetworkPreferenceCallbacks(
@NonNull final TestableNetworkCallback defaultCallback,
@NonNull final TestableNetworkCallback systemDefaultCallback) {
- registerDefaultNetworkCallback(defaultCallback);
+ networkCallbackRule.registerDefaultNetworkCallback(defaultCallback);
runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
}
@@ -2950,18 +2944,18 @@
+ " unless device supports WiFi",
mPackageManager.hasSystemFeature(FEATURE_WIFI));
- final TestNetworkCallback cb = new TestNetworkCallback();
try {
// Wait for partial connectivity to be detected on the network
final Network network = preparePartialConnectivity();
- requestNetwork(makeWifiNetworkRequest(), cb);
+ final TestableNetworkCallback cb = networkCallbackRule.requestNetwork(
+ makeWifiNetworkRequest());
runAsShell(NETWORK_SETTINGS, () -> {
// The always bit is verified in NetworkAgentTest
mCm.setAcceptPartialConnectivity(network, false /* accept */, false /* always */);
});
// Reject partial connectivity network should cause the network being torn down
- assertEquals(network, cb.waitForLost());
+ assertEquals(network, cb.eventuallyExpect(CallbackEntry.LOST).getNetwork());
} finally {
mHttpServer.stop();
// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
@@ -2989,17 +2983,17 @@
assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
+ " unless device supports WiFi and telephony", canRunTest);
- final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
try {
// Ensure at least one default network candidate connected.
- mCtsNetUtils.connectToCell();
+ networkCallbackRule.requestCell();
final Network wifiNetwork = prepareUnvalidatedNetwork();
// Default network should not be wifi ,but checking that wifi is not the default doesn't
// guarantee that it won't become the default in the future.
assertNotEquals(wifiNetwork, mCm.getActiveNetwork());
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
runAsShell(NETWORK_SETTINGS, () -> {
mCm.setAcceptUnvalidated(wifiNetwork, false /* accept */, false /* always */);
});
@@ -3026,19 +3020,19 @@
assumeTrue("testSetAvoidUnvalidated cannot execute"
+ " unless device supports WiFi and telephony", canRunTest);
- final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
- final TestableNetworkCallback defaultCb = new TestableNetworkCallback();
final int previousAvoidBadWifi =
ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
allowBadWifi();
try {
- final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final Network cellNetwork = networkCallbackRule.requestCell();
final Network wifiNetwork = prepareValidatedNetwork();
- registerDefaultNetworkCallback(defaultCb);
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ final TestableNetworkCallback defaultCb =
+ networkCallbackRule.registerDefaultNetworkCallback();
+ final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
// Verify wifi is the default network.
defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
@@ -3101,20 +3095,11 @@
});
}
- private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout)
- throws Exception {
- final CompletableFuture<Network> future = new CompletableFuture();
- final NetworkCallback cb = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network n, NetworkCapabilities nc) {
- if (n.equals(network) && nc.hasCapability(expectedNetCap)) {
- future.complete(network);
- }
- }
- };
-
- registerNetworkCallback(new NetworkRequest.Builder().build(), cb);
- return future.get(timeout, TimeUnit.MILLISECONDS);
+ private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout) {
+ return networkCallbackRule.registerNetworkCallback(new NetworkRequest.Builder().build())
+ .eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, timeout,
+ cb -> cb.getNetwork().equals(network)
+ && cb.getCaps().hasCapability(expectedNetCap)).getNetwork();
}
private void prepareHttpServer() throws Exception {
@@ -3214,8 +3199,6 @@
if (supportWifi) {
mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
- } else {
- mCtsNetUtils.disconnectFromCell();
}
final CompletableFuture<Boolean> future = new CompletableFuture<>();
@@ -3226,7 +3209,7 @@
if (supportWifi) {
mCtsNetUtils.ensureWifiConnected();
} else {
- mCtsNetUtils.connectToCell();
+ networkCallbackRule.requestCell();
}
assertTrue(future.get(LISTEN_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS));
}, () -> {
@@ -3267,13 +3250,14 @@
// For testing mobile data preferred uids feature, it needs both wifi and cell network.
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- final Network cellNetwork = mCtsNetUtils.connectToCell();
- final TestableNetworkCallback defaultTrackingCb = new TestableNetworkCallback();
- final TestableNetworkCallback systemDefaultCb = new TestableNetworkCallback();
+ final Network cellNetwork = networkCallbackRule.requestCell();
final Handler h = new Handler(Looper.getMainLooper());
- runWithShellPermissionIdentity(() -> registerSystemDefaultNetworkCallback(
- systemDefaultCb, h), NETWORK_SETTINGS);
- registerDefaultNetworkCallback(defaultTrackingCb);
+ final TestableNetworkCallback systemDefaultCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
+ NETWORK_SETTINGS);
+
+ final TestableNetworkCallback defaultTrackingCb =
+ networkCallbackRule.registerDefaultNetworkCallback();
try {
// CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
@@ -3342,7 +3326,7 @@
// Create test network agent with restricted network.
final NetworkCapabilities nc = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
@@ -3376,23 +3360,23 @@
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// File a restricted network request with permission first to hold the connection.
- final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.requestNetwork(testRequest),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
// File another restricted network request without permission.
final TestableNetworkCallback restrictedNetworkCb = new TestableNetworkCallback();
final NetworkRequest restrictedRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
@@ -3409,7 +3393,7 @@
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
@@ -3427,13 +3411,13 @@
if (TestUtils.shouldTestTApis()) {
// Uid is in allowed list. Try file network request again.
- requestNetwork(restrictedRequest, restrictedNetworkCb);
+ networkCallbackRule.requestNetwork(restrictedRequest, restrictedNetworkCb);
// Verify that the network is restricted.
restrictedNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
}
} finally {
agent.unregister();
@@ -3731,58 +3715,4 @@
// shims, and @IgnoreUpTo does not check that.
assumeTrue(TestUtils.shouldTestSApis());
}
-
- private void unregisterRegisteredCallbacks() {
- for (NetworkCallback callback: mRegisteredCallbacks) {
- mCm.unregisterNetworkCallback(callback);
- }
- }
-
- private void registerDefaultNetworkCallback(NetworkCallback callback) {
- mCm.registerDefaultNetworkCallback(callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
- mCm.registerDefaultNetworkCallback(callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) {
- mCm.registerNetworkCallback(request, callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
- mCmShim.registerSystemDefaultNetworkCallback(callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback,
- Handler handler) throws Exception {
- mCmShim.registerDefaultNetworkCallbackForUid(uid, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestNetwork(NetworkRequest request, NetworkCallback callback) {
- mCm.requestNetwork(request, callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestNetwork(NetworkRequest request, NetworkCallback callback, int timeoutSec) {
- mCm.requestNetwork(request, callback, timeoutSec);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerBestMatchingNetworkCallback(NetworkRequest request,
- NetworkCallback callback, Handler handler) {
- mCm.registerBestMatchingNetworkCallback(request, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestBackgroundNetwork(NetworkRequest request, NetworkCallback callback,
- Handler handler) throws Exception {
- mCmShim.requestBackgroundNetwork(request, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
}
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index bca18f5..73f65e0 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -16,10 +16,14 @@
package android.net.cts;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
@@ -34,10 +38,12 @@
import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.OsConstants;
+import android.util.ArraySet;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.testutils.AutoReleaseNetworkCallbackRule;
import com.android.testutils.DeviceConfigRule;
import org.junit.Before;
@@ -45,11 +51,17 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
public class MultinetworkApiTest {
- @Rule
+ @Rule(order = 1)
public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule();
+ @Rule(order = 2)
+ public final AutoReleaseNetworkCallbackRule
+ mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule();
+
static {
System.loadLibrary("nativemultinetwork_jni");
}
@@ -74,9 +86,8 @@
private ContentResolver mCR;
private ConnectivityManager mCM;
private CtsNetUtils mCtsNetUtils;
- private String mOldMode;
- private String mOldDnsSpecifier;
private Context mContext;
+ private Network mRequestedCellNetwork;
@Before
public void setUp() throws Exception {
@@ -87,8 +98,8 @@
}
@Test
- public void testGetaddrinfo() throws ErrnoException {
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ public void testGetaddrinfo() throws Exception {
+ for (Network network : getTestableNetworks()) {
int errno = runGetaddrinfoCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -99,12 +110,12 @@
@Test
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
- public void testSetprocnetwork() throws ErrnoException {
+ public void testSetprocnetwork() throws Exception {
// Hopefully no prior test in this process space has set a default network.
assertNull(mCM.getProcessDefaultNetwork());
assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ for (Network network : getTestableNetworks()) {
mCM.setProcessDefaultNetwork(null);
assertNull(mCM.getProcessDefaultNetwork());
@@ -123,7 +134,7 @@
mCM.setProcessDefaultNetwork(null);
}
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ for (Network network : getTestableNetworks()) {
NetworkUtils.bindProcessToNetwork(0);
assertNull(mCM.getBoundNetworkForProcess());
@@ -143,8 +154,8 @@
@Test
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
- public void testSetsocknetwork() throws ErrnoException {
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ public void testSetsocknetwork() throws Exception {
+ for (Network network : getTestableNetworks()) {
int errno = runSetsocknetwork(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -154,8 +165,8 @@
}
@Test
- public void testNativeDatagramTransmission() throws ErrnoException {
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ public void testNativeDatagramTransmission() throws Exception {
+ for (Network network : getTestableNetworks()) {
int errno = runDatagramCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -165,7 +176,7 @@
}
@Test
- public void testNoSuchNetwork() {
+ public void testNoSuchNetwork() throws Exception {
final Network eNoNet = new Network(54321);
assertNull(mCM.getNetworkInfo(eNoNet));
@@ -178,9 +189,9 @@
}
@Test
- public void testNetworkHandle() {
+ public void testNetworkHandle() throws Exception {
// Test Network -> NetworkHandle -> Network results in the same Network.
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ for (Network network : getTestableNetworks()) {
long networkHandle = network.getNetworkHandle();
Network newNetwork = Network.fromNetworkHandle(networkHandle);
assertEquals(newNetwork, network);
@@ -203,9 +214,7 @@
@Test
public void testResNApi() throws Exception {
- final Network[] testNetworks = mCtsNetUtils.getTestableNetworks();
-
- for (Network network : testNetworks) {
+ for (Network network : getTestableNetworks()) {
// Throws AssertionError directly in jni function if test fail.
runResNqueryCheck(network.getNetworkHandle());
runResNsendCheck(network.getNetworkHandle());
@@ -241,7 +250,7 @@
// b/144521720
try {
mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
- for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ for (Network network : getTestableNetworks()) {
// Wait for private DNS setting to propagate.
mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
network, GOOGLE_PRIVATE_DNS_SERVER, true);
@@ -251,4 +260,44 @@
mCtsNetUtils.restorePrivateDnsSetting();
}
}
+
+ /**
+ * Get all testable Networks with internet capability.
+ */
+ private Set<Network> getTestableNetworks() throws InterruptedException {
+ // Obtain cell and Wi-Fi through CtsNetUtils (which uses NetworkCallbacks), as they may have
+ // just been reconnected by the test using NetworkCallbacks, so synchronous calls may not
+ // yet return them (synchronous calls and callbacks should not be mixed for a given
+ // Network).
+ final Set<Network> testableNetworks = new ArraySet<>();
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY)) {
+ if (mRequestedCellNetwork == null) {
+ mRequestedCellNetwork = mNetworkCallbackRule.requestCell();
+ }
+ assertNotNull("Cell network requested but not obtained", mRequestedCellNetwork);
+ testableNetworks.add(mRequestedCellNetwork);
+ }
+
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI)) {
+ testableNetworks.add(mCtsNetUtils.ensureWifiConnected());
+ }
+
+ // Obtain other networks through the synchronous API, if any.
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
+ if (nc != null
+ && !nc.hasTransport(TRANSPORT_WIFI)
+ && !nc.hasTransport(TRANSPORT_CELLULAR)) {
+ testableNetworks.add(network);
+ }
+ }
+
+ // In practice this should not happen as getTestableNetworks throws if there is no network
+ // at all.
+ assertFalse("This device does not support WiFi nor cell data, and does not have any other "
+ + "network connected. This test requires at least one internet-providing "
+ + "network.",
+ testableNetworks.isEmpty());
+ return testableNetworks;
+ }
}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 3d828a4..670889f 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -408,40 +408,10 @@
return network;
}
- public Network connectToCell() throws InterruptedException {
- if (cellConnectAttempted()) {
- mCm.unregisterNetworkCallback(mCellNetworkCallback);
- }
- NetworkRequest cellRequest = new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_INTERNET)
- .build();
- mCellNetworkCallback = new TestNetworkCallback();
- mCm.requestNetwork(cellRequest, mCellNetworkCallback);
- final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
- assertNotNull("Cell network not available. " +
- "Please ensure the device has working mobile data.", cellNetwork);
- return cellNetwork;
- }
-
- public void disconnectFromCell() {
- if (!cellConnectAttempted()) {
- throw new IllegalStateException("Cell connection not attempted");
- }
- mCm.unregisterNetworkCallback(mCellNetworkCallback);
- mCellNetworkCallback = null;
- }
-
public boolean cellConnectAttempted() {
return mCellNetworkCallback != null;
}
- public void tearDown() {
- if (cellConnectAttempted()) {
- disconnectFromCell();
- }
- }
-
private NetworkRequest makeWifiNetworkRequest() {
return new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.kt b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.kt
new file mode 100644
index 0000000..8f86f06
--- /dev/null
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.nsd
+
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit tests for {@link NsdServiceInfo}. */
+@SmallTest
+@ConnectivityModuleTest
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class NsdServiceInfoTest {
+ @Test
+ fun testToString_txtRecord() {
+ val info = NsdServiceInfo().apply {
+ this.setAttribute("abc", byteArrayOf(0xff.toByte(), 0xfe.toByte()))
+ this.setAttribute("def", null as String?)
+ this.setAttribute("ghi", "猫")
+ this.setAttribute("jkl", byteArrayOf(0, 0x21))
+ this.setAttribute("mno", "Hey Tom! It's you?.~{}")
+ }
+
+ val infoStr = info.toString()
+
+ assertTrue(
+ infoStr.contains("txtRecord: " +
+ "{abc=0xFFFE, def=(null), ghi=0xE78CAB, jkl=0x0021, mno=Hey Tom! It's you?.~{}}"),
+ infoStr)
+ }
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 17c5901..7822fe0 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -172,6 +172,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
+import static com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN;
import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.ConcurrentUtils.await;
@@ -391,6 +392,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
@@ -2171,6 +2173,8 @@
return true;
case INGRESS_TO_VPN_ADDRESS_FILTERING:
return true;
+ case BACKGROUND_FIREWALL_CHAIN:
+ return true;
default:
return super.isFeatureNotChickenedOut(context, name);
}
@@ -10488,7 +10492,10 @@
doTestSetUidFirewallRule(FIREWALL_CHAIN_POWERSAVE, FIREWALL_RULE_DENY);
doTestSetUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, FIREWALL_RULE_DENY);
doTestSetUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, FIREWALL_RULE_DENY);
- doTestSetUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, FIREWALL_RULE_DENY);
+ if (SdkLevel.isAtLeastV()) {
+ // FIREWALL_CHAIN_BACKGROUND is only available on V+.
+ doTestSetUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, FIREWALL_RULE_DENY);
+ }
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_1, FIREWALL_RULE_ALLOW);
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_2, FIREWALL_RULE_ALLOW);
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_3, FIREWALL_RULE_ALLOW);
@@ -10496,16 +10503,19 @@
@Test @IgnoreUpTo(SC_V2)
public void testSetFirewallChainEnabled() throws Exception {
- final List<Integer> firewallChains = Arrays.asList(
+ final List<Integer> firewallChains = new ArrayList<>(Arrays.asList(
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_STANDBY,
FIREWALL_CHAIN_POWERSAVE,
FIREWALL_CHAIN_RESTRICTED,
FIREWALL_CHAIN_LOW_POWER_STANDBY,
- FIREWALL_CHAIN_BACKGROUND,
FIREWALL_CHAIN_OEM_DENY_1,
FIREWALL_CHAIN_OEM_DENY_2,
- FIREWALL_CHAIN_OEM_DENY_3);
+ FIREWALL_CHAIN_OEM_DENY_3));
+ if (SdkLevel.isAtLeastV()) {
+ // FIREWALL_CHAIN_BACKGROUND is only available on V+.
+ firewallChains.add(FIREWALL_CHAIN_BACKGROUND);
+ }
for (final int chain: firewallChains) {
mCm.setFirewallChainEnabled(chain, true /* enabled */);
verify(mBpfNetMaps).setChildChain(chain, true /* enable */);
@@ -10552,7 +10562,10 @@
doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_POWERSAVE, allowlist);
doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_RESTRICTED, allowlist);
doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_LOW_POWER_STANDBY, allowlist);
- doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_BACKGROUND, allowlist);
+ if (SdkLevel.isAtLeastV()) {
+ // FIREWALL_CHAIN_BACKGROUND is only available on V+.
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_BACKGROUND, allowlist);
+ }
doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_STANDBY, denylist);
doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_OEM_DENY_1, denylist);
@@ -10574,7 +10587,10 @@
doTestReplaceFirewallChain(FIREWALL_CHAIN_POWERSAVE);
doTestReplaceFirewallChain(FIREWALL_CHAIN_RESTRICTED);
doTestReplaceFirewallChain(FIREWALL_CHAIN_LOW_POWER_STANDBY);
- doTestReplaceFirewallChain(FIREWALL_CHAIN_BACKGROUND);
+ if (SdkLevel.isAtLeastV()) {
+ // FIREWALL_CHAIN_BACKGROUND is only available on V+.
+ doTestReplaceFirewallChain(FIREWALL_CHAIN_BACKGROUND);
+ }
doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_1);
doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_2);
doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_3);
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 881de56..d91e29c 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -521,6 +521,56 @@
}
@Test
+ @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
+ public void testDiscoverOnTetheringDownstream_DiscoveryManager() throws Exception {
+ final NsdManager client = connectClient(mService);
+ final DiscoveryListener discListener = mock(DiscoveryListener.class);
+ client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
+ waitForIdle();
+
+ final ArgumentCaptor<MdnsServiceBrowserListener> discoverListenerCaptor =
+ ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final InOrder discManagerOrder = inOrder(mDiscoveryManager);
+ final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
+ discManagerOrder.verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
+ discoverListenerCaptor.capture(), any());
+
+ final int interfaceIdx = 123;
+ final MdnsServiceInfo mockServiceInfo = new MdnsServiceInfo(
+ SERVICE_NAME, /* serviceInstanceName */
+ serviceTypeWithLocalDomain.split("\\."), /* serviceType */
+ List.of(), /* subtypes */
+ new String[] {"android", "local"}, /* hostName */
+ 12345, /* port */
+ List.of(IPV4_ADDRESS),
+ List.of(IPV6_ADDRESS),
+ List.of(), /* textStrings */
+ List.of(), /* textEntries */
+ interfaceIdx, /* interfaceIndex */
+ null /* network */,
+ Instant.MAX /* expirationTime */);
+
+ // Verify service is found with the interface index
+ discoverListenerCaptor.getValue().onServiceNameDiscovered(
+ mockServiceInfo, false /* isServiceFromCache */);
+ final ArgumentCaptor<NsdServiceInfo> foundInfoCaptor =
+ ArgumentCaptor.forClass(NsdServiceInfo.class);
+ verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(foundInfoCaptor.capture());
+ final NsdServiceInfo foundInfo = foundInfoCaptor.getValue();
+ assertNull(foundInfo.getNetwork());
+ assertEquals(interfaceIdx, foundInfo.getInterfaceIndex());
+
+ // Using the returned service info to resolve or register callback uses the interface index
+ client.resolveService(foundInfo, mock(ResolveListener.class));
+ client.registerServiceInfoCallback(foundInfo, Runnable::run,
+ mock(ServiceInfoCallback.class));
+ waitForIdle();
+
+ discManagerOrder.verify(mDiscoveryManager, times(2)).registerListener(any(), any(), argThat(
+ o -> o.getNetwork() == null && o.getInterfaceIndex() == interfaceIdx));
+ }
+
+ @Test
@DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
@DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public void testDiscoverOnBlackholeNetwork() throws Exception {
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
index 5251e2a..b5c0132 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -18,6 +18,8 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -65,8 +67,9 @@
private static final String SERVICE_TYPE_2 = "_test._tcp.local";
private static final Network NETWORK_1 = Mockito.mock(Network.class);
private static final Network NETWORK_2 = Mockito.mock(Network.class);
+ private static final int INTERFACE_INDEX_NULL_NETWORK = 123;
private static final SocketKey SOCKET_KEY_NULL_NETWORK =
- new SocketKey(null /* network */, 999 /* interfaceIndex */);
+ new SocketKey(null /* network */, INTERFACE_INDEX_NULL_NETWORK);
private static final SocketKey SOCKET_KEY_NETWORK_1 =
new SocketKey(NETWORK_1, 998 /* interfaceIndex */);
private static final SocketKey SOCKET_KEY_NETWORK_2 =
@@ -97,6 +100,8 @@
private HandlerThread thread;
private Handler handler;
+ private int createdServiceTypeClientCount;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -106,11 +111,13 @@
handler = new Handler(thread.getLooper());
doReturn(thread.getLooper()).when(socketClient).getLooper();
doReturn(true).when(socketClient).supportsRequestingSpecificNetworks();
+ createdServiceTypeClientCount = 0;
discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient,
sharedLog, MdnsFeatureFlags.newBuilder().build()) {
@Override
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@NonNull SocketKey socketKey) {
+ createdServiceTypeClientCount++;
final Pair<String, SocketKey> perSocketServiceType =
Pair.create(serviceType, socketKey);
if (perSocketServiceType.equals(PER_SOCKET_SERVICE_TYPE_1_NULL_NETWORK)) {
@@ -128,6 +135,7 @@
PER_SOCKET_SERVICE_TYPE_2_NETWORK_2)) {
return mockServiceTypeClientType2Network2;
}
+ fail("Unexpected perSocketServiceType: " + perSocketServiceType);
return null;
}
};
@@ -324,7 +332,6 @@
// Receive a response, it should be processed on the client.
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
- final int ifIndex = 1;
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).processResponse(
response, SOCKET_KEY_NULL_NETWORK);
@@ -350,6 +357,39 @@
verify(socketClient, never()).stopDiscovery();
}
+ @Test
+ public void testInterfaceIndexRequested_OnlyUsesSelectedInterface() throws IOException {
+ final MdnsSearchOptions searchOptions =
+ MdnsSearchOptions.newBuilder()
+ .setNetwork(null /* network */)
+ .setInterfaceIndex(INTERFACE_INDEX_NULL_NETWORK)
+ .build();
+
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, searchOptions);
+ final SocketKey unusedIfaceKey = new SocketKey(null, INTERFACE_INDEX_NULL_NETWORK + 1);
+ final SocketKey matchingIfaceWithNetworkKey =
+ new SocketKey(Mockito.mock(Network.class), INTERFACE_INDEX_NULL_NETWORK);
+ runOnHandler(() -> {
+ callback.onSocketCreated(unusedIfaceKey);
+ callback.onSocketCreated(matchingIfaceWithNetworkKey);
+ callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK);
+ callback.onSocketCreated(SOCKET_KEY_NETWORK_1);
+ });
+ // Only the client for INTERFACE_INDEX_NULL_NETWORK is created
+ verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(
+ mockListenerOne, searchOptions);
+ assertEquals(1, createdServiceTypeClientCount);
+
+ runOnHandler(() -> {
+ callback.onSocketDestroyed(SOCKET_KEY_NETWORK_1);
+ callback.onSocketDestroyed(SOCKET_KEY_NULL_NETWORK);
+ callback.onSocketDestroyed(matchingIfaceWithNetworkKey);
+ callback.onSocketDestroyed(unusedIfaceKey);
+ });
+ verify(mockServiceTypeClientType1NullNetwork).notifySocketDestroyed();
+ }
+
private MdnsPacket createMdnsPacket(String serviceType) {
final String[] type = TextUtils.split(serviceType, "\\.");
final ArrayList<String> name = new ArrayList<>(type.length + 1);
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt
new file mode 100644
index 0000000..16de4da
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.net.ConnectivityManager
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.any
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class CSFirewallChainTest : CSTest() {
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule()
+
+ // Tests for setFirewallChainEnabled on FIREWALL_CHAIN_BACKGROUND
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)])
+ fun setFirewallChainEnabled_backgroundChainDisabled() {
+ verifySetFirewallChainEnabledOnBackgroundDoesNothing()
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setFirewallChainEnabled_backgroundChainEnabled_afterU() {
+ cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
+ verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
+
+ clearInvocations(bpfNetMaps)
+
+ cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
+ verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setFirewallChainEnabled_backgroundChainEnabled_uptoU() {
+ verifySetFirewallChainEnabledOnBackgroundDoesNothing()
+ }
+
+ private fun verifySetFirewallChainEnabledOnBackgroundDoesNothing() {
+ cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
+ verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean())
+
+ cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
+ verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean())
+ }
+
+ // Tests for replaceFirewallChain on FIREWALL_CHAIN_BACKGROUND
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)])
+ fun replaceFirewallChain_backgroundChainDisabled() {
+ verifyReplaceFirewallChainOnBackgroundDoesNothing()
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun replaceFirewallChain_backgroundChainEnabled_afterU() {
+ val uids = intArrayOf(53, 42, 79)
+ cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
+ verify(bpfNetMaps).replaceUidChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun replaceFirewallChain_backgroundChainEnabled_uptoU() {
+ verifyReplaceFirewallChainOnBackgroundDoesNothing()
+ }
+
+ private fun verifyReplaceFirewallChainOnBackgroundDoesNothing() {
+ val uids = intArrayOf(53, 42, 79)
+ cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
+ verify(bpfNetMaps, never()).replaceUidChain(anyInt(), any(IntArray::class.java))
+ }
+
+ // Tests for setUidFirewallRule on FIREWALL_CHAIN_BACKGROUND
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)])
+ fun setUidFirewallRule_backgroundChainDisabled() {
+ verifySetUidFirewallRuleOnBackgroundDoesNothing()
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setUidFirewallRule_backgroundChainEnabled_afterU() {
+ val uid = 2345
+
+ cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_DEFAULT)
+ verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_DENY)
+
+ clearInvocations(bpfNetMaps)
+
+ cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_DENY)
+ verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_DENY)
+
+ clearInvocations(bpfNetMaps)
+
+ cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_ALLOW)
+ verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
+ ConnectivityManager.FIREWALL_RULE_ALLOW)
+ }
+
+ @Test
+ @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
+ @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setUidFirewallRule_backgroundChainEnabled_uptoU() {
+ verifySetUidFirewallRuleOnBackgroundDoesNothing()
+ }
+
+ private fun verifySetUidFirewallRuleOnBackgroundDoesNothing() {
+ val uid = 2345
+
+ listOf(ConnectivityManager.FIREWALL_RULE_DEFAULT, ConnectivityManager.FIREWALL_RULE_ALLOW,
+ ConnectivityManager.FIREWALL_RULE_DENY).forEach { rule ->
+ cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, rule)
+ verify(bpfNetMaps, never()).setUidRule(anyInt(), anyInt(), anyInt())
+ }
+ }
+}
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 bd26c63..3b06ad0 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -161,6 +161,7 @@
it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
it[ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK] = true
it[ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING] = true
+ it[ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN] = true
}
fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 949e0c2..70d4ad8 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -554,4 +554,22 @@
mNetworkOfferCallback.onNetworkNeeded(createDefaultRequest());
verify(mIpClient, never()).startProvisioning(any());
}
+
+ @Test
+ public void testGetMacAddressProvisionedInterface() throws Exception {
+ initEthernetNetworkFactory();
+ createAndVerifyProvisionedInterface(TEST_IFACE);
+
+ final String result = mNetFactory.getHwAddress(TEST_IFACE);
+ assertEquals(HW_ADDR, result);
+ }
+
+ @Test
+ public void testGetMacAddressForNonExistingInterface() {
+ initEthernetNetworkFactory();
+
+ final String result = mNetFactory.getHwAddress(TEST_IFACE);
+ // No interface exists due to not calling createAndVerifyProvisionedInterface(...).
+ assertNull(result);
+ }
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index d80dcfb..0559499 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -73,7 +73,6 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalNetworkConfig;
import android.net.LocalNetworkInfo;
@@ -106,7 +105,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserManager;
import android.util.Log;
import android.util.SparseArray;
@@ -129,8 +127,6 @@
import java.io.IOException;
import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Instant;
@@ -165,6 +161,9 @@
// Note that this regex allows "XX:XX-XX" as well but we don't need to be a strict checker
private static final String OUI_REGEX = "^([0-9A-Fa-f]{2}[:-]?){2}([0-9A-Fa-f]{2})$";
+ // The channel mask that indicates all channels from channel 11 to channel 24
+ private static final int CHANNEL_MASK_11_TO_24 = 0x1FFF800;
+
// Below member fields can be accessed from both the binder and handler threads
private final Context mContext;
@@ -260,38 +259,6 @@
countryCodeSupplier);
}
- private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
- try {
- return (Inet6Address) Inet6Address.getByAddress(addressBytes);
- } catch (UnknownHostException e) {
- // This is unlikely to happen unless the Thread daemon is critically broken
- return null;
- }
- }
-
- private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
- return bytesToInet6Address(addressInfo.address);
- }
-
- private static LinkAddress newLinkAddress(Ipv6AddressInfo addressInfo) {
- long deprecationTimeMillis =
- addressInfo.isPreferred
- ? LinkAddress.LIFETIME_PERMANENT
- : SystemClock.elapsedRealtime();
-
- InetAddress address = addressInfoToInetAddress(addressInfo);
-
- // flags and scope will be adjusted automatically depending on the address and
- // its lifetimes.
- return new LinkAddress(
- address,
- addressInfo.prefixLength,
- 0 /* flags */,
- 0 /* scope */,
- deprecationTimeMillis,
- LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
- }
-
private NetworkRequest newUpstreamNetworkRequest() {
NetworkRequest.Builder builder = new NetworkRequest.Builder().clearCapabilities();
@@ -699,6 +666,7 @@
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
.addCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.build();
final NetworkScore score =
@@ -825,6 +793,14 @@
private static int selectChannel(
int supportedChannelMask, int preferredChannelMask, Random random) {
+ // Due to radio hardware performance reasons, many Thread radio chips need to reduce their
+ // transmit power on edge channels to pass regulatory RF certification. Thread edge channel
+ // 25 and 26 are not preferred here.
+ //
+ // If users want to use channel 25 or 26, they can change the channel via the method
+ // ActiveOperationalDataset.Builder(activeOperationalDataset).setChannel(channel).build().
+ preferredChannelMask = preferredChannelMask & CHANNEL_MASK_11_TO_24;
+
// If the preferred channel mask is not empty, select a random channel from it, otherwise
// choose one from the supported channel mask.
preferredChannelMask = preferredChannelMask & supportedChannelMask;
@@ -1171,20 +1147,10 @@
}
}
- private void handleAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
+ private void handleAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
checkOnHandlerThread();
- InetAddress address = addressInfoToInetAddress(addressInfo);
- if (address.isMulticastAddress()) {
- Log.i(TAG, "Ignoring multicast address " + address.getHostAddress());
- return;
- }
- LinkAddress linkAddress = newLinkAddress(addressInfo);
- if (isAdded) {
- mTunIfController.addAddress(linkAddress);
- } else {
- mTunIfController.removeAddress(linkAddress);
- }
+ mTunIfController.updateAddresses(addressInfoList);
// The OT daemon can send link property updates before the networkAgent is
// registered
@@ -1533,8 +1499,8 @@
}
@Override
- public void onAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
- mHandler.post(() -> handleAddressChanged(addressInfo, isAdded));
+ public void onAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
+ mHandler.post(() -> handleAddressChanged(addressInfoList));
}
@Override
diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java
index b29a54f..97cdd55 100644
--- a/thread/service/java/com/android/server/thread/TunInterfaceController.java
+++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java
@@ -29,12 +29,19 @@
import android.system.OsConstants;
import android.util.Log;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.netlink.NetlinkUtils;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.server.thread.openthread.Ipv6AddressInfo;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
/** Controller for virtual/tunnel network interfaces. */
public class TunInterfaceController {
@@ -178,6 +185,38 @@
}
}
+ public void updateAddresses(List<Ipv6AddressInfo> addressInfoList) {
+ final List<LinkAddress> newLinkAddresses = new ArrayList<>();
+ boolean hasActiveOmrAddress = false;
+
+ for (Ipv6AddressInfo addressInfo : addressInfoList) {
+ if (addressInfo.isActiveOmr) {
+ hasActiveOmrAddress = true;
+ break;
+ }
+ }
+
+ for (Ipv6AddressInfo addressInfo : addressInfoList) {
+ InetAddress address = addressInfoToInetAddress(addressInfo);
+ if (address.isMulticastAddress()) {
+ // TODO: Logging here will create repeated logs for a single multicast address, and
+ // it currently is not mandatory for debugging. Add log for ignored multicast
+ // address when necessary.
+ continue;
+ }
+ newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress));
+ }
+
+ final CompareResult<LinkAddress> addressDiff =
+ new CompareResult<>(mLinkProperties.getAllLinkAddresses(), newLinkAddresses);
+ for (LinkAddress linkAddress : addressDiff.removed) {
+ removeAddress(linkAddress);
+ }
+ for (LinkAddress linkAddress : addressDiff.added) {
+ addAddress(linkAddress);
+ }
+ }
+
private RouteInfo getRouteForAddress(LinkAddress linkAddress) {
return new RouteInfo(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()),
@@ -195,4 +234,44 @@
Log.e(TAG, "Failed to set Thread TUN interface down");
}
}
+
+ private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
+ return bytesToInet6Address(addressInfo.address);
+ }
+
+ private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
+ try {
+ return (Inet6Address) Inet6Address.getByAddress(addressBytes);
+ } catch (UnknownHostException e) {
+ // This is unlikely to happen unless the Thread daemon is critically broken
+ return null;
+ }
+ }
+
+ private static LinkAddress newLinkAddress(
+ Ipv6AddressInfo addressInfo, boolean hasActiveOmrAddress) {
+ // Mesh-local addresses and OMR address have the same scope, to distinguish them we set
+ // mesh-local addresses as deprecated when there is an active OMR address.
+ // For OMR address and link-local address we only use the value isPreferred set by
+ // ot-daemon.
+ boolean isPreferred = addressInfo.isPreferred;
+ if (addressInfo.isMeshLocal && hasActiveOmrAddress) {
+ isPreferred = false;
+ }
+
+ final long deprecationTimeMillis =
+ isPreferred ? LinkAddress.LIFETIME_PERMANENT : SystemClock.elapsedRealtime();
+
+ final InetAddress address = addressInfoToInetAddress(addressInfo);
+
+ // flags and scope will be adjusted automatically depending on the address and
+ // its lifetimes.
+ return new LinkAddress(
+ address,
+ addressInfo.prefixLength,
+ 0 /* flags */,
+ 0 /* scope */,
+ deprecationTimeMillis,
+ LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
+ }
}
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index 9a81388..0e7f3be 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -17,6 +17,12 @@
package android.net.thread.cts;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_CHILD;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_ROUTER;
@@ -73,6 +79,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -743,17 +750,6 @@
}
@Test
- public void setEnabled_toggleAfterJoin_joinsThreadNetworkAgain() throws Exception {
- joinRandomizedDatasetAndWait(mController);
-
- setEnabledAndWait(mController, false);
- assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED);
- setEnabledAndWait(mController, true);
-
- runAsShell(ACCESS_NETWORK_STATE, () -> waitForAttachedState(mController));
- }
-
- @Test
public void setEnabled_enableFollowedByDisable_allSucceed() throws Exception {
joinRandomizedDatasetAndWait(mController);
CompletableFuture<Void> setFuture1 = new CompletableFuture<>();
@@ -805,6 +801,20 @@
assertThat(isAttached(mController)).isTrue();
assertThat(networkFuture.get(NETWORK_CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNotNull();
+ NetworkCapabilities caps =
+ runAsShell(
+ ACCESS_NETWORK_STATE, () -> cm.getNetworkCapabilities(networkFuture.get()));
+ assertThat(caps).isNotNull();
+ assertThat(caps.hasTransport(NetworkCapabilities.TRANSPORT_THREAD)).isTrue();
+ assertThat(caps.getCapabilities())
+ .asList()
+ .containsAtLeast(
+ NET_CAPABILITY_LOCAL_NETWORK,
+ NET_CAPABILITY_NOT_METERED,
+ NET_CAPABILITY_NOT_RESTRICTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
+ NET_CAPABILITY_NOT_VPN,
+ NET_CAPABILITY_TRUSTED);
}
private void grantPermissions(String... permissions) {
@@ -837,6 +847,7 @@
}
@Test
+ @Ignore("b/333649897: Enable this when it's not flaky at all")
public void meshcopService_joinedNetwork_discoveredHasNetwork() throws Exception {
setUpTestNetwork();
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index 9b1c338..8c63d37 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
@@ -33,6 +34,7 @@
import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -43,6 +45,8 @@
import android.content.Context;
import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.thread.utils.FullThreadDevice;
@@ -55,6 +59,7 @@
import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
@@ -252,6 +257,21 @@
}
@Test
+ public void unicastRouting_meshLocalAddressesAreNotPreferred() throws Exception {
+ // When BR is enabled, there will be OMR address, so the mesh-local addresses are expected
+ // to be deprecated.
+ List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
+ IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
+
+ for (LinkAddress address : linkAddresses) {
+ if (meshLocalPrefix.contains(address.getAddress())) {
+ assertThat(address.getDeprecationTime()).isAtMost(SystemClock.elapsedRealtime());
+ assertThat(address.isPreferred()).isFalse();
+ }
+ }
+ }
+
+ @Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdSubscribedMulticastAddress_infraLinkJoinsMulticastGroup()
throws Exception {
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index 491331c..e10f134 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -75,7 +75,6 @@
@RequiresThreadFeature
@RequiresSimulationThreadDevice
@LargeTest
-@Ignore("TODO: b/328527773 - enable the test when it's stable")
public class ServiceDiscoveryTest {
private static final String TAG = ServiceDiscoveryTest.class.getSimpleName();
private static final int NUM_FTD = 3;
@@ -103,11 +102,13 @@
private HandlerThread mHandlerThread;
private NsdManager mNsdManager;
private TapTestNetworkTracker mTestNetworkTracker;
- private List<FullThreadDevice> mFtds;
- private List<RegistrationListener> mRegistrationListeners = new ArrayList<>();
+ private final List<FullThreadDevice> mFtds = new ArrayList<>();
+ private final List<RegistrationListener> mRegistrationListeners = new ArrayList<>();
@Before
public void setUp() throws Exception {
+ mOtCtl.factoryReset();
+ mController.setEnabledAndWait(true);
mController.joinAndWait(DEFAULT_DATASET);
mNsdManager = mContext.getSystemService(NsdManager.class);
@@ -120,7 +121,6 @@
// Create the FTDs in setUp() so that the FTDs can be safely released in tearDown().
// Don't create new FTDs in test cases.
- mFtds = new ArrayList<>();
for (int i = 0; i < NUM_FTD; ++i) {
FullThreadDevice ftd = new FullThreadDevice(10 + i /* node ID */);
ftd.autoStartSrpClient();
@@ -265,6 +265,7 @@
}
@Test
+ @Ignore("TODO: b/333806992 - Enable when it's not flaky at all")
public void advertisingProxy_srpClientUnregistersService_serviceIsNotDiscoverableByMdns()
throws Exception {
/*
@@ -321,6 +322,7 @@
}
@Test
+ @Ignore("TODO: b/332452386 - Enable this test case when it handles the multi-client case well")
public void discoveryProxy_multipleClientsBrowseAndResolveServiceOverMdns() throws Exception {
/*
* <pre>
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index c70f3af..1410d41 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -21,6 +21,8 @@
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED;
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
+import static android.net.thread.utils.IntegrationTestUtils.waitFor;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
@@ -29,6 +31,9 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
@@ -45,14 +50,28 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.time.Duration;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/** Tests for E2E Android Thread integration with ot-daemon, ConnectivityService, etc.. */
@LargeTest
@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public class ThreadIntegrationTest {
+ // The byte[] buffer size for UDP tests
+ private static final int UDP_BUFFER_SIZE = 1024;
+
+ // The maximum time for OT addresses to be propagated to the TUN interface "thread-wpan"
+ private static final Duration TUN_ADDR_UPDATE_TIMEOUT = Duration.ofSeconds(1);
+
// A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
private static final byte[] DEFAULT_DATASET_TLVS =
base16().decode(
@@ -66,24 +85,32 @@
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+ private ExecutorService mExecutor;
private final Context mContext = ApplicationProvider.getApplicationContext();
private final ThreadNetworkControllerWrapper mController =
ThreadNetworkControllerWrapper.newInstance(mContext);
private OtDaemonController mOtCtl;
+ private FullThreadDevice mFtd;
@Before
public void setUp() throws Exception {
+ mExecutor = Executors.newSingleThreadExecutor();
mOtCtl = new OtDaemonController();
mController.leaveAndWait();
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
mOtCtl.factoryReset();
+
+ mFtd = new FullThreadDevice(10 /* nodeId */);
}
@After
public void tearDown() throws Exception {
mController.setTestNetworkAsUpstreamAndWait(null);
mController.leaveAndWait();
+
+ mFtd.destroy();
+ mExecutor.shutdownNow();
}
@Test
@@ -129,16 +156,22 @@
assertThat(ifconfig).doesNotContain("inet6 addr");
}
+ // TODO (b/323300829): add test for removing an OT address
@Test
public void tunInterface_joinedNetwork_otAddressesAddedToTunInterface() throws Exception {
mController.joinAndWait(DEFAULT_DATASET);
- String ifconfig = runShellCommand("ifconfig thread-wpan");
List<Inet6Address> otAddresses = mOtCtl.getAddresses();
assertThat(otAddresses).isNotEmpty();
- for (Inet6Address otAddress : otAddresses) {
- assertThat(ifconfig).contains(otAddress.getHostAddress());
- }
+ // TODO: it's cleaner to have a retry() method to retry failed asserts in given delay so
+ // that we can write assertThat() in the Predicate
+ waitFor(
+ () -> {
+ String ifconfig = runShellCommand("ifconfig thread-wpan");
+ return otAddresses.stream()
+ .allMatch(addr -> ifconfig.contains(addr.getHostAddress()));
+ },
+ TUN_ADDR_UPDATE_TIMEOUT);
}
@Test
@@ -153,10 +186,77 @@
assertThat(mOtCtl.getCountryCode()).isEqualTo("CN");
}
- private static String runThreadCommand(String cmd) {
- return runShellCommandOrThrow("cmd thread_network " + cmd);
+ @Test
+ public void udp_appStartEchoServer_endDeviceUdpEchoSuccess() throws Exception {
+ // Topology:
+ // Test App ------ thread-wpan ------ End Device
+
+ mController.joinAndWait(DEFAULT_DATASET);
+ startFtdChild(mFtd, DEFAULT_DATASET);
+ final Inet6Address serverAddress = mOtCtl.getMeshLocalAddresses().get(0);
+ final int serverPort = 9527;
+
+ mExecutor.execute(() -> startUdpEchoServerAndWait(serverAddress, serverPort));
+ mFtd.udpOpen();
+ mFtd.udpSend("Hello,Thread", serverAddress, serverPort);
+ String udpReply = mFtd.udpReceive();
+
+ assertThat(udpReply).isEqualTo("Hello,Thread");
+ }
+
+ @Test
+ public void joinNetworkWithBrDisabled_meshLocalAddressesArePreferred() throws Exception {
+ // When BR feature is disabled, there is no OMR address, so the mesh-local addresses are
+ // expected to be preferred.
+ mOtCtl.executeCommand("br disable");
+ mController.joinAndWait(DEFAULT_DATASET);
+
+ IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
+ List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
+ for (LinkAddress address : linkAddresses) {
+ if (meshLocalPrefix.contains(address.getAddress())) {
+ assertThat(address.getDeprecationTime())
+ .isGreaterThan(SystemClock.elapsedRealtime());
+ assertThat(address.isPreferred()).isTrue();
+ }
+ }
+
+ mOtCtl.executeCommand("br enable");
}
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService
+
+ private static String runThreadCommand(String cmd) {
+ return runShellCommandOrThrow("cmd thread_network " + cmd);
+ }
+
+ private void startFtdChild(FullThreadDevice ftd, ActiveOperationalDataset activeDataset)
+ throws Exception {
+ ftd.factoryReset();
+ ftd.joinNetwork(activeDataset);
+ ftd.waitForStateAnyOf(List.of("router", "child"), Duration.ofSeconds(8));
+ }
+
+ /**
+ * Starts a UDP echo server and replies to the first UDP message.
+ *
+ * <p>This method exits when the first UDP message is received and the reply is sent
+ */
+ private void startUdpEchoServerAndWait(InetAddress serverAddress, int serverPort) {
+ try (var udpServerSocket = new DatagramSocket(serverPort, serverAddress)) {
+ DatagramPacket recvPacket =
+ new DatagramPacket(new byte[UDP_BUFFER_SIZE], UDP_BUFFER_SIZE);
+ udpServerSocket.receive(recvPacket);
+ byte[] sendBuffer = Arrays.copyOf(recvPacket.getData(), recvPacket.getData().length);
+ udpServerSocket.send(
+ new DatagramPacket(
+ sendBuffer,
+ sendBuffer.length,
+ (Inet6Address) recvPacket.getAddress(),
+ recvPacket.getPort()));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index f7bb9ff..5e70f6c 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -20,8 +20,6 @@
import static com.google.common.io.BaseEncoding.base16;
-import static org.junit.Assert.fail;
-
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.nsd.NsdServiceInfo;
@@ -218,6 +216,11 @@
return matcher.group(4);
}
+ /** Sends a UDP message to given IPv6 address and port. */
+ public void udpSend(String message, Inet6Address serverAddr, int serverPort) {
+ executeCommand("udp send %s %d %s", serverAddr.getHostAddress(), serverPort, message);
+ }
+
/** Enables the SRP client and run in autostart mode. */
public void autoStartSrpClient() {
executeCommand("srp client autostart enable");
@@ -474,7 +477,7 @@
break;
}
if (line.startsWith("Error")) {
- fail("ot-cli-ftd reported an error: " + line);
+ throw new IOException("ot-cli-ftd reported an error: " + line);
}
if (!line.startsWith("> ")) {
result.add(line);
diff --git a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
index 2237e65..9be9566 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -25,6 +25,8 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
import android.net.TestNetworkInterface;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
@@ -85,7 +87,7 @@
*/
public static void waitFor(Supplier<Boolean> condition, Duration timeout)
throws TimeoutException {
- final long intervalMills = 1000;
+ final long intervalMills = 500;
final long timeoutMills = timeout.toMillis();
for (long i = 0; i < timeoutMills; i += intervalMills) {
@@ -291,6 +293,20 @@
return false;
}
+ public static List<LinkAddress> getIpv6LinkAddresses(String interfaceName) throws IOException {
+ List<LinkAddress> addresses = new ArrayList<>();
+ final String cmd = " ip -6 addr show dev " + interfaceName;
+ final String output = runShellCommandOrThrow(cmd);
+
+ for (final String line : output.split("\\n")) {
+ if (line.contains("inet6")) {
+ addresses.add(parseAddressLine(line));
+ }
+ }
+
+ return addresses;
+ }
+
/** Return the first discovered service of {@code serviceType}. */
public static NsdServiceInfo discoverService(NsdManager nsdManager, String serviceType)
throws Exception {
@@ -392,4 +408,29 @@
@Override
public void onServiceInfoCallbackUnregistered() {}
}
+
+ /**
+ * Parses a line of output from "ip -6 addr show" into a {@link LinkAddress}.
+ *
+ * <p>Example line: "inet6 2001:db8:1:1::1/64 scope global deprecated"
+ */
+ private static LinkAddress parseAddressLine(String line) {
+ String[] parts = line.trim().split("\\s+");
+ String addressString = parts[1];
+ String[] pieces = addressString.split("/", 2);
+ int prefixLength = Integer.parseInt(pieces[1]);
+ final InetAddress address = InetAddresses.parseNumericAddress(pieces[0]);
+ long deprecationTimeMillis =
+ line.contains("deprecated")
+ ? SystemClock.elapsedRealtime()
+ : LinkAddress.LIFETIME_PERMANENT;
+
+ return new LinkAddress(
+ address,
+ prefixLength,
+ 0 /* flags */,
+ 0 /* scope */,
+ deprecationTimeMillis,
+ LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
+ }
}
diff --git a/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java b/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
index f39a064..b3175fd 100644
--- a/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
+++ b/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
@@ -16,13 +16,16 @@
package android.net.thread.utils;
+import android.annotation.Nullable;
import android.net.InetAddresses;
+import android.net.IpPrefix;
import android.os.SystemClock;
import com.android.compatibility.common.util.SystemUtil;
import java.net.Inet6Address;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
@@ -53,10 +56,7 @@
/** Returns the list of IPv6 addresses on ot-daemon. */
public List<Inet6Address> getAddresses() {
- String output = executeCommand("ipaddr");
- return Arrays.asList(output.split("\n")).stream()
- .map(String::trim)
- .filter(str -> !str.equals("Done"))
+ return executeCommandAndParse("ipaddr").stream()
.map(addr -> InetAddresses.parseNumericAddress(addr))
.map(inetAddr -> (Inet6Address) inetAddr)
.toList();
@@ -70,17 +70,54 @@
/** Returns the ML-EID of the device. */
public Inet6Address getMlEid() {
- String addressStr = executeCommand("ipaddr mleid").split("\n")[0].trim();
+ String addressStr = executeCommandAndParse("ipaddr mleid").get(0);
return (Inet6Address) InetAddresses.parseNumericAddress(addressStr);
}
/** Returns the country code on ot-daemon. */
public String getCountryCode() {
- String countryCodeStr = executeCommand("region").split("\n")[0].trim();
- return countryCodeStr;
+ return executeCommandAndParse("region").get(0);
+ }
+
+ /**
+ * Returns the list of IPv6 Mesh-Local addresses on ot-daemon.
+ *
+ * <p>The return List can be empty if no Mesh-Local prefix exists.
+ */
+ public List<Inet6Address> getMeshLocalAddresses() {
+ IpPrefix meshLocalPrefix = getMeshLocalPrefix();
+ if (meshLocalPrefix == null) {
+ return Collections.emptyList();
+ }
+ return getAddresses().stream().filter(addr -> meshLocalPrefix.contains(addr)).toList();
+ }
+
+ /**
+ * Returns the Mesh-Local prefix or {@code null} if none exists (e.g. the Active Dataset is not
+ * set).
+ */
+ @Nullable
+ public IpPrefix getMeshLocalPrefix() {
+ List<IpPrefix> prefixes =
+ executeCommandAndParse("prefix meshlocal").stream()
+ .map(prefix -> new IpPrefix(prefix))
+ .toList();
+ return prefixes.isEmpty() ? null : prefixes.get(0);
}
public String executeCommand(String cmd) {
return SystemUtil.runShellCommand(OT_CTL + " " + cmd);
}
+
+ /**
+ * Executes a ot-ctl command and parse the output to a list of strings.
+ *
+ * <p>The trailing "Done" in the command output will be dropped.
+ */
+ public List<String> executeCommandAndParse(String cmd) {
+ return Arrays.asList(executeCommand(cmd).split("\n")).stream()
+ .map(String::trim)
+ .filter(str -> !str.equals("Done"))
+ .toList();
+ }
}
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index 85b6873..493058f 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -115,7 +115,11 @@
private static final String DEFAULT_NETWORK_NAME = "thread-wpan0";
private static final int OT_ERROR_NONE = 0;
private static final int DEFAULT_SUPPORTED_CHANNEL_MASK = 0x07FFF800; // from channel 11 to 26
- private static final int DEFAULT_PREFERRED_CHANNEL_MASK = 0x00000800; // channel 11
+
+ // The DEFAULT_PREFERRED_CHANNEL_MASK is the ot-daemon preferred channel mask. Channel 25 and
+ // 26 are not preferred by dataset. The ThreadNetworkControllerService will only select channel
+ // 11 when it creates randomized dataset.
+ private static final int DEFAULT_PREFERRED_CHANNEL_MASK = 0x06000800; // channel 11, 25 and 26
private static final int DEFAULT_SELECTED_CHANNEL = 11;
private static final byte[] DEFAULT_SUPPORTED_CHANNEL_MASK_ARRAY = base16().decode("001FFFE0");