Add unit tests for race conditions in upstream selection. am: 6748e62ef2 am: afb685e650 am: 4321aa067b
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1662399
Change-Id: Ie7fb4e07a2c571218871a6560f77bbc985280c1e
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ef96d88..389453a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -15,7 +15,8 @@
],
"mainline-presubmit": [
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ // TODO: add back the tethering modules when updatable in this branch
+ "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -26,7 +27,8 @@
// Tests on physical devices with SIM cards: postsubmit only for capacity constraints
"mainline-postsubmit": [
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ // TODO: add back the tethering module when updatable in this branch
+ "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]",
"keywords": ["sim"]
}
]
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 742fd02..4eafc2a 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -25,7 +25,7 @@
srcs: [
"apishim/**/*.java",
"src/**/*.java",
- ":framework-tethering-shared-srcs",
+ ":framework-connectivity-shared-srcs",
":tethering-module-utils-srcs",
":services-tethering-shared-srcs",
],
@@ -41,6 +41,7 @@
"netd-client",
],
libs: [
+ "framework-connectivity",
"framework-statsd.stubs.module_lib",
"framework-tethering.impl",
"framework-wifi",
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 6703e46..5be3933 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -20,14 +20,30 @@
apex {
name: "com.android.tethering",
+ compile_multilib: "both",
updatable: true,
min_sdk_version: "30",
- java_libs: ["framework-tethering"],
+ java_libs: [
+ "framework-connectivity",
+ "framework-tethering",
+ "service-connectivity",
+ ],
+ multilib: {
+ first: {
+ jni_libs: ["libservice-connectivity"]
+ },
+ both: {
+ jni_libs: ["libframework-connectivity-jni"],
+ }
+ },
bpfs: [
"offload.o",
"test.o",
],
- apps: ["Tethering"],
+ apps: [
+ "ServiceConnectivityResources",
+ "Tethering",
+ ],
manifest: "manifest.json",
key: "com.android.tethering.key",
@@ -50,6 +66,7 @@
base: "com.android.tethering",
package_name: "com.android.tethering.inprocess",
apps: [
+ "ServiceConnectivityResources",
"InProcessTethering",
],
}
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 2631d08..6bff616 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -26,6 +26,13 @@
],
srcs: [":framework-tethering-srcs"],
+ libs: ["framework-connectivity.stubs.module_lib"],
+ stub_only_libs: ["framework-connectivity.stubs.module_lib"],
+ aidl: {
+ include_dirs: [
+ "frameworks/base/packages/Connectivity/framework/aidl-export",
+ ],
+ },
jarjar_rules: "jarjar-rules.txt",
installable: true,
diff --git a/Tethering/jarjar-rules.txt b/Tethering/jarjar-rules.txt
index d1ad569..5de4b97 100644
--- a/Tethering/jarjar-rules.txt
+++ b/Tethering/jarjar-rules.txt
@@ -1,5 +1,5 @@
-# These must be kept in sync with the framework-tethering-shared-srcs filegroup.
-# Classes from the framework-tethering-shared-srcs filegroup.
+# These must be kept in sync with the framework-connectivity-shared-srcs filegroup.
+# Classes from the framework-connectivity-shared-srcs filegroup.
# If there are files in that filegroup that are not covered below, the classes in the
# module will be overwritten by the ones in the framework.
rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index c6e8fd6..f795747 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1719,6 +1719,12 @@
break;
}
+ if (mConfig.chooseUpstreamAutomatically
+ && arg1 == UpstreamNetworkMonitor.EVENT_DEFAULT_SWITCHED) {
+ chooseUpstreamType(true);
+ return;
+ }
+
if (ns == null || !pertainsToCurrentUpstream(ns)) {
// TODO: In future, this is where upstream evaluation and selection
// could be handled for notifications which include sufficient data.
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 1622175..f3cd549 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -302,10 +302,8 @@
boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
@NonNull String callingPackage, @Nullable String callingAttributionTag,
boolean throwException) {
- // TODO: on S and above, pass the attribution tag to Settings instead of throwing it away.
- // This will likely require a SettingsShim class.
return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
- throwException);
+ callingAttributionTag, throwException);
}
/**
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 513f07c..e39145b 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -42,11 +42,14 @@
import android.util.Log;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
@@ -82,6 +85,7 @@
public static final int EVENT_ON_CAPABILITIES = 1;
public static final int EVENT_ON_LINKPROPERTIES = 2;
public static final int EVENT_ON_LOST = 3;
+ public static final int EVENT_DEFAULT_SWITCHED = 4;
public static final int NOTIFY_LOCAL_PREFIXES = 10;
// This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
// disabled.
@@ -398,7 +402,7 @@
notifyTarget(EVENT_ON_CAPABILITIES, network);
}
- private void handleLinkProp(Network network, LinkProperties newLp) {
+ private void updateLinkProperties(Network network, LinkProperties newLp) {
final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newLp.equals(prev.linkProperties)) {
// Ignore notifications about networks for which we have not yet
@@ -414,8 +418,10 @@
mNetworkMap.put(network, new UpstreamNetworkState(
newLp, prev.networkCapabilities, network));
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target.
+ }
+
+ private void handleLinkProp(Network network, LinkProperties newLp) {
+ updateLinkProperties(network, newLp);
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
}
@@ -445,6 +451,24 @@
notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
}
+ private void maybeHandleNetworkSwitch(@NonNull Network network) {
+ if (Objects.equals(mDefaultInternetNetwork, network)) return;
+
+ final UpstreamNetworkState ns = mNetworkMap.get(network);
+ if (ns == null) {
+ // Can never happen unless there is a bug in ConnectivityService. Entries are only
+ // removed from mNetworkMap when receiving onLost, and onLost for a given network can
+ // never be followed by any other callback on that network.
+ Log.wtf(TAG, "maybeHandleNetworkSwitch: no UpstreamNetworkState for " + network);
+ return;
+ }
+
+ // Default network changed. Update local data and notify tethering.
+ Log.d(TAG, "New default Internet network: " + network);
+ mDefaultInternetNetwork = network;
+ notifyTarget(EVENT_DEFAULT_SWITCHED, ns);
+ }
+
private void recomputeLocalPrefixes() {
final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
if (!mLocalPrefixes.equals(localPrefixes)) {
@@ -482,7 +506,22 @@
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
- mDefaultInternetNetwork = network;
+ // mDefaultInternetNetwork is not updated here because upstream selection must only
+ // run when the LinkProperties have been updated as well as the capabilities. If
+ // this callback is due to a default network switch, then the system will invoke
+ // onLinkPropertiesChanged right after this method and mDefaultInternetNetwork will
+ // be updated then.
+ //
+ // Technically, not updating here isn't necessary, because the notifications to
+ // Tethering sent by notifyTarget are messages sent to a state machine running on
+ // the same thread as this method, and so cannot arrive until after this method has
+ // returned. However, it is not a good idea to rely on that because fact that
+ // Tethering uses multiple state machines running on the same thread is a major
+ // source of race conditions and something that should be fixed.
+ //
+ // TODO: is it correct that this code always updates EntitlementManager?
+ // This code runs when the default network connects or changes capabilities, but the
+ // default network might not be the tethering upstream.
final boolean newIsCellular = isCellular(newNc);
if (mIsDefaultCellularUpstream != newIsCellular) {
mIsDefaultCellularUpstream = newIsCellular;
@@ -496,7 +535,15 @@
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
+ if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
+ updateLinkProperties(network, newLp);
+ // When the default network callback calls onLinkPropertiesChanged, it means that
+ // all the network information for the default network is known (because
+ // onLinkPropertiesChanged is called after onAvailable and onCapabilitiesChanged).
+ // Inform tethering that the default network might have changed.
+ maybeHandleNetworkSwitch(network);
+ return;
+ }
handleLinkProp(network, newLp);
// Any non-LISTEN_ALL callback will necessarily concern a network that will
@@ -513,6 +560,8 @@
mDefaultInternetNetwork = null;
mIsDefaultCellularUpstream = false;
mEntitlementMgr.notifyUpstream(false);
+ Log.d(TAG, "Lost default Internet network: " + network);
+ notifyTarget(EVENT_DEFAULT_SWITCHED, null);
return;
}
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index edb6356..8c7e101 100644
--- a/Tethering/tests/mts/Android.bp
+++ b/Tethering/tests/mts/Android.bp
@@ -47,7 +47,7 @@
"libstaticjvmtiagent",
],
- platform_apis: true,
+ defaults: ["framework-connectivity-test-defaults"],
// Tag this module as a mts test artifact
test_suites: [
diff --git a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 42a91aa..a933e1b 100644
--- a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -21,9 +21,8 @@
import static com.android.net.module.util.IpUtils.icmpv6Checksum;
import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
import android.content.Context;
@@ -52,13 +51,14 @@
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import java.io.IOException;
import java.nio.ByteBuffer;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DadProxyTest {
private static final int DATA_BUFFER_LEN = 4096;
- private static final int PACKET_TIMEOUT_MS = 5_000;
+ private static final int PACKET_TIMEOUT_MS = 2_000; // Long enough for DAD to succeed.
// Start the readers manually on a common handler shared with DadProxy, for simplicity
@Rule
@@ -119,16 +119,18 @@
}
}
- private void setupTapInterfaces() {
+ private void setupTapInterfaces() throws Exception {
// Create upstream test iface.
mUpstreamReader.start(mHandler);
- mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName());
+ final String upstreamIface = mUpstreamReader.iface.getInterfaceName();
+ mUpstreamParams = InterfaceParams.getByName(upstreamIface);
assertNotNull(mUpstreamParams);
mUpstreamPacketReader = mUpstreamReader.getReader();
// Create tethered test iface.
mTetheredReader.start(mHandler);
- mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName());
+ final String tetheredIface = mTetheredReader.getIface().getInterfaceName();
+ mTetheredParams = InterfaceParams.getByName(tetheredIface);
assertNotNull(mTetheredParams);
mTetheredPacketReader = mTetheredReader.getReader();
}
@@ -224,6 +226,12 @@
return false;
}
+ private ByteBuffer copy(ByteBuffer buf) {
+ // There does not seem to be a way to copy ByteBuffers. ByteBuffer does not implement
+ // clone() and duplicate() copies the metadata but shares the contents.
+ return ByteBuffer.wrap(buf.array().clone());
+ }
+
private void updateDstMac(ByteBuffer buf, MacAddress mac) {
buf.put(mac.toByteArray());
buf.rewind();
@@ -234,14 +242,50 @@
buf.rewind();
}
+ private void receivePacketAndMaybeExpectForwarded(boolean expectForwarded,
+ ByteBuffer in, TapPacketReader inReader, ByteBuffer out, TapPacketReader outReader)
+ throws IOException {
+
+ inReader.sendResponse(in);
+ if (waitForPacket(out, outReader)) return;
+
+ // When the test runs, DAD may be in progress, because the interface has just been created.
+ // If so, the DAD proxy will get EADDRNOTAVAIL when trying to send packets. It is not
+ // possible to work around this using IPV6_FREEBIND or IPV6_TRANSPARENT options because the
+ // kernel rawv6 code doesn't consider those options either when binding or when sending, and
+ // doesn't get the source address from the packet even in IPPROTO_RAW/HDRINCL mode (it only
+ // gets it from the socket or from cmsg).
+ //
+ // If DAD was in progress when the above was attempted, try again and expect the packet to
+ // be forwarded. Don't disable DAD in the test because if we did, the test would not notice
+ // if, for example, the DAD proxy code just crashed if it received EADDRNOTAVAIL.
+ final String msg = expectForwarded
+ ? "Did not receive expected packet even after waiting for DAD:"
+ : "Unexpectedly received packet:";
+
+ inReader.sendResponse(in);
+ assertEquals(msg, expectForwarded, waitForPacket(out, outReader));
+ }
+
+ private void receivePacketAndExpectForwarded(ByteBuffer in, TapPacketReader inReader,
+ ByteBuffer out, TapPacketReader outReader) throws IOException {
+ receivePacketAndMaybeExpectForwarded(true, in, inReader, out, outReader);
+ }
+
+ private void receivePacketAndExpectNotForwarded(ByteBuffer in, TapPacketReader inReader,
+ ByteBuffer out, TapPacketReader outReader) throws IOException {
+ receivePacketAndMaybeExpectForwarded(false, in, inReader, out, outReader);
+ }
+
@Test
public void testNaForwardingFromUpstreamToTether() throws Exception {
ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
- mUpstreamPacketReader.sendResponse(na);
- updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
- updateSrcMac(na, mTetheredParams);
- assertTrue(waitForPacket(na, mTetheredPacketReader));
+ ByteBuffer out = copy(na);
+ updateDstMac(out, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(out, mTetheredParams);
+
+ receivePacketAndExpectForwarded(na, mUpstreamPacketReader, out, mTetheredPacketReader);
}
@Test
@@ -249,19 +293,21 @@
public void testNaForwardingFromTetherToUpstream() throws Exception {
ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
- mTetheredPacketReader.sendResponse(na);
- updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
- updateSrcMac(na, mTetheredParams);
- assertFalse(waitForPacket(na, mUpstreamPacketReader));
+ ByteBuffer out = copy(na);
+ updateDstMac(out, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(out, mTetheredParams);
+
+ receivePacketAndExpectNotForwarded(na, mTetheredPacketReader, out, mUpstreamPacketReader);
}
@Test
public void testNsForwardingFromTetherToUpstream() throws Exception {
ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
- mTetheredPacketReader.sendResponse(ns);
- updateSrcMac(ns, mUpstreamParams);
- assertTrue(waitForPacket(ns, mUpstreamPacketReader));
+ ByteBuffer out = copy(ns);
+ updateSrcMac(out, mUpstreamParams);
+
+ receivePacketAndExpectForwarded(ns, mTetheredPacketReader, out, mUpstreamPacketReader);
}
@Test
@@ -269,8 +315,9 @@
public void testNsForwardingFromUpstreamToTether() throws Exception {
ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
- mUpstreamPacketReader.sendResponse(ns);
+ ByteBuffer out = copy(ns);
updateSrcMac(ns, mUpstreamParams);
- assertFalse(waitForPacket(ns, mTetheredPacketReader));
+
+ receivePacketAndExpectNotForwarded(ns, mUpstreamPacketReader, out, mTetheredPacketReader);
}
}
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index d469b37..011b878 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -33,6 +33,7 @@
sdk_version: "core_platform",
libs: [
"framework-minus-apex",
+ "framework-connectivity.impl",
"framework-tethering.impl",
],
visibility: [
@@ -66,6 +67,7 @@
"ext",
"framework-minus-apex",
"framework-res",
+ "framework-connectivity.impl",
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
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 3f847ff..d18d990 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -1120,37 +1120,26 @@
mLooper.dispatchAll();
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
+ // This code has historically been racy, so test different orderings of CONNECTIVITY_ACTION
+ // broadcasts and callbacks, and add mLooper.dispatchAll() calls between the two.
final Runnable doDispatchAll = () -> mLooper.dispatchAll();
- // There is a race where if Tethering and UpstreamNetworkMonitor process the
- // CONNECTIVITY_ACTION before UpstreamNetworkMonitor gets onCapabilitiesChanged on
- // CALLBACK_DEFAULT_INTERNET, the upstream does not change.
- // Simulate this by telling TestConnectivityManager to call mLooper.dispatchAll() between
- // sending the CONNECTIVITY_ACTION and sending the callbacks.
- // The switch to wifi just above shows that if the CONNECTIVITY_ACTION and the callbacks
- // happen close enough together that the tethering code sees both, the code behaves as
- // expected.
mCm.makeDefaultNetwork(mobile, BROADCAST_FIRST, doDispatchAll);
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any()); // BUG
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
mCm.makeDefaultNetwork(wifi, BROADCAST_FIRST, doDispatchAll);
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId); // BUG
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
- // If the broadcast immediately follows the callbacks, the code behaves as expected.
- // In this case nothing happens because the upstream is already set to cellular and
- // remaining on cellular is correct.
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
mLooper.dispatchAll();
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
- // If the broadcast happens after the callbacks have been processed, the code also behaves
- // correctly.
mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST, doDispatchAll);
mLooper.dispatchAll();
inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
@@ -1161,31 +1150,30 @@
inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
// Lose and regain upstream.
- // As above, if broadcasts are processed before the callbacks, upstream is not updated.
assertTrue(mUpstreamNetworkMonitor.getCurrentPreferredUpstream().linkProperties
.hasIPv4Address());
mCm.makeDefaultNetwork(null, BROADCAST_FIRST, doDispatchAll);
mLooper.dispatchAll();
mobile.fakeDisconnect();
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any()); // BUG
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
mobile = new TestNetworkAgent(mCm, buildMobile464xlatUpstreamState());
mobile.fakeConnect();
mCm.makeDefaultNetwork(mobile, BROADCAST_FIRST, doDispatchAll);
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null); // BUG
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
// Check the IP addresses to ensure that the upstream is indeed not the same as the previous
// mobile upstream, even though the netId is (unrealistically) the same.
assertFalse(mUpstreamNetworkMonitor.getCurrentPreferredUpstream().linkProperties
.hasIPv4Address());
- // Lose and regain upstream again, but this time send events in an order that works.
+ // Lose and regain upstream again.
mCm.makeDefaultNetwork(null, CALLBACKS_FIRST, doDispatchAll);
mobile.fakeDisconnect();
mLooper.dispatchAll();
- inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any()); // BUG
+ inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
mobile.fakeConnect();
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index b7fefaf..7a73313 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -23,7 +23,10 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.cts.net.NetworkPolicyTestsPreparer" />
+ <!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat enable ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
+ <option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
<option name="teardown-command" value="cmd power set-mode 0" />
<option name="teardown-command" value="cmd battery reset" />
<option name="teardown-command" value="cmd netpolicy stop-watching" />
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 5aafdf0..fbbb68b 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
@@ -16,6 +16,8 @@
package com.android.cts.net.hostside;
+import android.app.job.JobInfo;
+
import com.android.cts.net.hostside.INetworkCallback;
interface IMyService {
@@ -26,4 +28,5 @@
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in INetworkCallback cb);
void unregisterNetworkCallback();
+ void scheduleJob(in JobInfo jobInfo);
}
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 165f530..19198c5 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
@@ -17,6 +17,10 @@
package com.android.cts.net.hostside;
interface INetworkStateObserver {
- boolean isForeground();
- void onNetworkStateChecked(String resultData);
+ void onNetworkStateChecked(int resultCode, String resultData);
+
+ const int RESULT_SUCCESS_NETWORK_STATE_CHECKED = 0;
+ const int RESULT_ERROR_UNEXPECTED_PROC_STATE = 1;
+ const int RESULT_ERROR_UNEXPECTED_CAPABILITIES = 2;
+ const int RESULT_ERROR_OTHER = 3;
}
\ No newline at end of file
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 50fda6d..f351b47 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -37,10 +37,6 @@
"android.test.base",
],
srcs: ["src/**/*.java"],
- // STOPSHIP: remove this before releasing any networking modules.
- exclude_srcs: [
- "src/com/android/cts/net/hostside/VpnTest.java",
- ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index f9e30b6..d9ff539 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -50,7 +50,7 @@
public final void tearDown() throws Exception {
super.tearDown();
- executeSilentShellCommand("cmd battery reset");
+ resetBatteryState();
setAppIdle(false);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index 6f32c56..e0ce4ea 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -101,7 +101,7 @@
@Test
public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
throws Exception {
- setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
+ setPendingIntentAllowlistDuration(NETWORK_TIMEOUT_MS);
try {
registerNotificationListenerService();
setDozeMode(true);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
new file mode 100644
index 0000000..a850e3b
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkTestCase {
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testNetworkAccess_batterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testNetworkAccess_appIdleState() throws Exception {
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testNetworkAccess_dozeMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataAndBatterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE, DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dozeAndDataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK, DOZE_MODE,
+ APP_STANDBY_MODE})
+ public void testNetworkAccess_allRestrictionsEnabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ setAppIdle(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+}
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 48923b9..140d8cd 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
@@ -22,6 +22,7 @@
import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
@@ -39,6 +40,7 @@
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.NotificationManager;
+import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -51,13 +53,19 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BatteryUtils;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
import org.junit.Rule;
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;
@@ -74,12 +82,22 @@
private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
+ private static final String TEST_APP2_JOB_SERVICE_CLASS = TEST_APP2_PKG + ".MyJobService";
+
+ private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(
+ TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS);
+
+ private static final int TEST_JOB_ID = 7357437;
private static final int SLEEP_TIME_SEC = 1;
// Constants below must match values defined on app2's Common.java
private static final String MANIFEST_RECEIVER = "ManifestReceiver";
private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ private static final String ACTION_FINISH_ACTIVITY =
+ "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ private static final String ACTION_FINISH_JOB =
+ "com.android.cts.net.hostside.app2.action.FINISH_JOB";
private static final String ACTION_RECEIVER_READY =
"com.android.cts.net.hostside.app2.action.RECEIVER_READY";
@@ -101,17 +119,21 @@
private static final String NETWORK_STATUS_SEPARATOR = "\\|";
private static final int SECOND_IN_MS = 1000;
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+
private static int PROCESS_STATE_FOREGROUND_SERVICE;
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+ private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ protected static final int TYPE_EXPEDITED_JOB = 2;
private static final int BATTERY_STATE_TIMEOUT_MS = 5000;
private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500;
- private static final int FOREGROUND_PROC_NETWORK_TIMEOUT_MS = 6000;
+ private static final int ACTIVITY_NETWORK_STATE_TIMEOUT_MS = 6_000;
+ private static final int JOB_NETWORK_STATE_TIMEOUT_MS = 10_000;
// Must be higher than NETWORK_TIMEOUT_MS
private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
@@ -129,24 +151,25 @@
protected int mUid;
private int mMyUid;
private MyServiceClient mServiceClient;
- private String mDeviceIdleConstantsSetting;
+ private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper;
@Rule
public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule())
.around(new MeterednessConfigurationRule());
protected void setUp() throws Exception {
-
+ // TODO: Annotate these constants with @TestApi instead of obtaining them using reflection
PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
.getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
mInstrumentation = getInstrumentation();
mContext = getContext();
mCm = getConnectivityManager();
+ mDeviceIdleDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_DEVICE_IDLE);
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
mServiceClient = new MyServiceClient(mContext);
mServiceClient.bind();
- mDeviceIdleConstantsSetting = "device_idle_constants";
executeShellCommand("cmd netpolicy start-watching " + mUid);
setAppIdle(false);
@@ -253,8 +276,8 @@
/**
* Asserts that an app always have access while on foreground or running a foreground service.
*
- * <p>This method will launch an activity and a foreground service to make the assertion, but
- * will finish the activity / stop the service afterwards.
+ * <p>This method will launch an activity, a foreground service to make
+ * the assertion, but will finish the activity / stop the service afterwards.
*/
protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
// Checks foreground first.
@@ -266,6 +289,16 @@
stopForegroundService();
}
+ protected void assertExpeditedJobHasNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB);
+ finishExpeditedJob();
+ }
+
+ protected void assertExpeditedJobHasNoNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false);
+ finishExpeditedJob();
+ }
+
protected final void assertBackgroundState() throws Exception {
final int maxTries = 30;
ProcessState state = null;
@@ -337,7 +370,7 @@
for (int i = 1; i <= maxTries; i++) {
error = checkNetworkAccess(expectAvailable);
- if (error.isEmpty()) return;
+ if (error == null) return;
// TODO: ideally, it should retry only when it cannot connect to an external site,
// or no retry at all! But, currently, the initial change fails almost always on
@@ -409,7 +442,7 @@
errors.append("\tnetworkInfo: " + networkInfo + "\n");
errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
}
- return errors.toString();
+ return errors.length() == 0 ? null : errors.toString();
}
/**
@@ -601,6 +634,10 @@
assertBatteryState(true);
}
+ protected void resetBatteryState() {
+ BatteryUtils.runDumpsysBatteryReset();
+ }
+
private void assertBatteryState(boolean pluggedIn) throws Exception {
final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS;
while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) {
@@ -734,18 +771,21 @@
nm.isNotificationListenerAccessGranted(listenerComponent));
}
- protected void setPendingIntentWhitelistDuration(int durationMs) throws Exception {
- executeSilentShellCommand(String.format(
- "settings put global %s %s=%d", mDeviceIdleConstantsSetting,
- "notification_whitelist_duration", durationMs));
+ protected void setPendingIntentAllowlistDuration(long durationMs) {
+ mDeviceIdleDeviceConfigStateHelper.set("notification_allowlist_duration_ms",
+ String.valueOf(durationMs));
}
- protected void resetDeviceIdleSettings() throws Exception {
- executeShellCommand(String.format("settings delete global %s",
- mDeviceIdleConstantsSetting));
+ protected void resetDeviceIdleSettings() {
+ mDeviceIdleDeviceConfigStateHelper.restoreOriginalValues();
}
protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
+ launchComponentAndAssertNetworkAccess(type, true);
+ }
+
+ protected void launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable)
+ throws Exception {
if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
startForegroundService();
assertForegroundServiceNetworkAccess();
@@ -757,21 +797,61 @@
final CountDownLatch latch = new CountDownLatch(1);
final Intent launchIntent = getIntentForComponent(type);
final Bundle extras = new Bundle();
- final String[] errors = new String[]{null};
- extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
+ final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
launchIntent.putExtras(extras);
mContext.startActivity(launchIntent);
- if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- if (!errors[0].isEmpty()) {
- if (errors[0] == APP_NOT_FOREGROUND_ERROR) {
- // App didn't come to foreground when the activity is started, so try again.
- assertForegroundNetworkAccess();
- } else {
- fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+ if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ final int resultCode = result.get(0).first;
+ final String resultData = result.get(0).second;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInResultData(
+ resultData, expectAvailable);
+ 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);
+ // App didn't come to foreground when the activity is started, so try again.
+ assertForegroundNetworkAccess();
+ } else {
+ fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
}
} else {
- fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
+ fail("Timed out waiting for network availability status from app2's activity ("
+ + mUid + ")");
+ }
+ } else if (type == TYPE_EXPEDITED_JOB) {
+ final Bundle extras = new Bundle();
+ final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ final CountDownLatch latch = new CountDownLatch(1);
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT)
+ .setExpedited(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setTransientExtras(extras)
+ .build();
+ 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;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInResultData(
+ resultData, expectAvailable);
+ if (error != null) {
+ fail("Network is not available for expedited job in app2 (" + mUid + "): "
+ + error);
+ }
+ } else {
+ fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2's expedited job ("
+ + mUid + ")");
}
} else {
throw new IllegalArgumentException("Unknown type: " + type);
@@ -805,36 +885,34 @@
}
private Binder getNewNetworkStateObserver(final CountDownLatch latch,
- final String[] errors) {
+ final ArrayList<Pair<Integer, String>> result) {
return new INetworkStateObserver.Stub() {
@Override
- public boolean isForeground() {
- try {
- final ProcessState state = getProcessStateByUid(mUid);
- return !isBackground(state.state);
- } catch (Exception e) {
- Log.d(TAG, "Error while reading the proc state for " + mUid + ": " + e);
- return false;
- }
- }
-
- @Override
- public void onNetworkStateChecked(String resultData) {
- errors[0] = resultData == null
- ? APP_NOT_FOREGROUND_ERROR
- : checkForAvailabilityInResultData(resultData, true);
+ public void onNetworkStateChecked(int resultCode, String resultData) {
+ result.add(Pair.create(resultCode, resultData));
latch.countDown();
}
};
}
/**
- * Finishes an activity on app2 so its process is demoted fromforeground status.
+ * Finishes an activity on app2 so its process is demoted from foreground status.
*/
protected void finishActivity() throws Exception {
- executeShellCommand("am broadcast -a "
- + " com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY "
- + "--receiver-foreground --receiver-registered-only");
+ final Intent intent = new Intent(ACTION_FINISH_ACTIVITY)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
+ }
+
+ /**
+ * Finishes the expedited job on app2 so its process is demoted from foreground status.
+ */
+ private void finishExpeditedJob() throws Exception {
+ final Intent intent = new Intent(ACTION_FINISH_JOB)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
}
protected void sendNotification(int notificationId, String notificationType) throws Exception {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
new file mode 100644
index 0000000..3809534
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class ExpeditedJobMeteredTest extends AbstractExpeditedJobTest {
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
new file mode 100644
index 0000000..6596269
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ExpeditedJobNonMeteredTest extends AbstractExpeditedJobTest {
+}
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 6546e26..1339be6 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
@@ -16,6 +16,7 @@
package com.android.cts.net.hostside;
+import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -104,4 +105,8 @@
public void unregisterNetworkCallback() throws RemoteException {
mService.unregisterNetworkCallback();
}
+
+ public void scheduleJob(JobInfo jobInfo) throws RemoteException {
+ mService.scheduleJob(jobInfo);
+ }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index 0a13408..9d1d418 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -48,6 +48,7 @@
import android.net.wifi.WifiManager.ActionListener;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.data.ApnSetting;
@@ -137,6 +138,12 @@
return am.isLowRamDevice();
}
+ /** Forces JobScheduler to run the job if constraints are met. */
+ public static void forceRunJob(String pkg, int jobId) {
+ executeShellCommand("cmd jobscheduler run -f -u " + UserHandle.myUserId()
+ + " " + pkg + " " + jobId);
+ }
+
public static boolean isLocationEnabled() {
final LocationManager lm = (LocationManager) getContext().getSystemService(
Context.LOCATION_SERVICE);
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index b448459..dd33eed 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -21,7 +21,7 @@
android_test_helper_app {
name: "CtsHostsideNetworkTestsApp2",
defaults: ["cts_support_defaults"],
- sdk_version: "current",
+ sdk_version: "test_current",
static_libs: ["CtsHostsideNetworkTestsAidl"],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index eb777f2..4ac4bcb 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -21,20 +21,24 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!--
- This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
- them in a shared preferences which is then read by the test app. These broadcasts are
- handled by 2 listeners, one defined the manifest and another dynamically registered by
- a service.
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
+ a service.
- The manifest-defined listener also handles ordered broadcasts used to share data with the
- test app.
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
- This application also provides a service, RemoteSocketFactoryService, that the test app can
- use to open sockets to remote hosts as a different user ID.
- -->
- <application android:usesCleartextTraffic="true">
+ This application also provides a service, RemoteSocketFactoryService, that the test app can
+ use to open sockets to remote hosts as a different user ID.
+ -->
+ <application android:usesCleartextTraffic="true"
+ android:testOnly="true"
+ android:debuggable="true">
+
<activity android:name=".MyActivity"
android:exported="true"/>
<service android:name=".MyService"
@@ -55,6 +59,8 @@
<action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST"/>
</intent-filter>
</receiver>
+ <service android:name=".MyJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
</manifest>
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 351733e..62b508c 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,11 +15,13 @@
*/
package com.android.cts.net.hostside.app2;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -38,6 +40,8 @@
"com.android.cts.net.hostside.app2.action.RECEIVER_READY";
static final String ACTION_FINISH_ACTIVITY =
"com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ static final String ACTION_FINISH_JOB =
+ "com.android.cts.net.hostside.app2.action.FINISH_JOB";
static final String ACTION_SHOW_TOAST =
"com.android.cts.net.hostside.app2.action.SHOW_TOAST";
@@ -51,6 +55,11 @@
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 int TYPE_COMPONENT_ACTIVTY = 0;
+ static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ static final int TYPE_COMPONENT_EXPEDITED_JOB = 2;
static int getUid(Context context) {
final String packageName = context.getPackageName();
@@ -61,11 +70,57 @@
}
}
- static void notifyNetworkStateObserver(Context context, Intent intent) {
+ private static boolean validateComponentState(Context context, int componentType,
+ INetworkStateObserver observer) throws RemoteException {
+ final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+ switch (componentType) {
+ 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);
+ return false;
+ }
+ return true;
+ }
+ 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);
+ return false;
+ }
+ return true;
+ }
+ case TYPE_COMPONENT_EXPEDITED_JOB: {
+ final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
+ if ((capabilities & ActivityManager.PROCESS_CAPABILITY_NETWORK) == 0) {
+ observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES,
+ "Unexpected capabilities: " + capabilities);
+ return false;
+ }
+ return true;
+ }
+ default: {
+ observer.onNetworkStateChecked(INetworkStateObserver.RESULT_ERROR_OTHER,
+ "Unknown component type: " + componentType);
+ return false;
+ }
+ }
+ }
+
+ static void notifyNetworkStateObserver(Context context, Intent intent, int componentType) {
if (intent == null) {
return;
}
final Bundle extras = intent.getExtras();
+ notifyNetworkStateObserver(context, extras, componentType);
+ }
+
+ static void notifyNetworkStateObserver(Context context, Bundle extras, int componentType) {
if (extras == null) {
return;
}
@@ -73,17 +128,17 @@
extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
if (observer != null) {
try {
- if (!observer.isForeground()) {
- Log.e(TAG, "App didn't come to foreground");
- observer.onNetworkStateChecked(null);
+ final boolean skipValidation = extras.getBoolean(KEY_SKIP_VALIDATION_CHECKS);
+ if (!skipValidation && !validateComponentState(context, componentType, observer)) {
return;
}
} catch (RemoteException e) {
- Log.e(TAG, "Error occurred while reading the proc state: " + e);
+ Log.e(TAG, "Error occurred while informing the validation result: " + e);
}
AsyncTask.execute(() -> {
try {
observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
MyBroadcastReceiver.checkNetworkStatus(context));
} catch (RemoteException e) {
Log.e(TAG, "Error occurred while notifying the observer: " + e);
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index 286cc2f..9fdb9c9 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -18,6 +18,7 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -42,7 +43,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MyActivity.onCreate()");
- Common.notifyNetworkStateObserver(this, getIntent());
+ Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
finishCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
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 aa54075..c9ae16f 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
@@ -201,7 +201,7 @@
Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
final Intent serviceIntent = new Intent(context, MyService.class);
final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
- notificationId);
+ PendingIntent.FLAG_MUTABLE);
final Bundle bundle = new Bundle();
bundle.putCharSequence("parcelable", "I am not");
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
index ff4ba65..b55761c 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -17,6 +17,7 @@
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
import android.R;
import android.app.Notification;
@@ -58,7 +59,7 @@
startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
.build());
- Common.notifyNetworkStateObserver(this, intent);
+ Common.notifyNetworkStateObserver(this, intent, TYPE_COMPONENT_FOREGROUND_SERVICE);
break;
case FLAG_STOP_FOREGROUND:
Log.d(TAG, "Stopping foreground");
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
new file mode 100644
index 0000000..51c3157
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_JOB;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+public class MyJobService extends JobService {
+
+ private BroadcastReceiver mFinishCommandReceiver = null;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.v(TAG, "MyJobService.onCreate()");
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.v(TAG, "MyJobService.onStartJob()");
+ Common.notifyNetworkStateObserver(this, params.getTransientExtras(),
+ TYPE_COMPONENT_EXPEDITED_JOB);
+ mFinishCommandReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "Finishing MyJobService");
+ try {
+ jobFinished(params, /*wantsReschedule=*/ false);
+ } finally {
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ }
+ }
+ };
+ registerReceiver(mFinishCommandReceiver, new IntentFilter(ACTION_FINISH_JOB));
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // If this job is stopped before it had a chance to send network status via
+ // INetworkStateObserver, the test will fail. It could happen either due to test timing out
+ // or this app moving to a lower proc_state and losing network access.
+ Log.v(TAG, "MyJobService.onStopJob()");
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.v(TAG, "MyJobService.onDestroy()");
+ }
+}
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 1c9ff05..9e10d41 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
@@ -24,6 +24,8 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -154,6 +156,13 @@
mNetworkCallback = null;
}
}
+
+ @Override
+ public void scheduleJob(JobInfo jobInfo) {
+ final JobScheduler jobScheduler = getApplicationContext()
+ .getSystemService(JobScheduler.class);
+ jobScheduler.schedule(jobInfo);
+ }
};
private NetworkRequest makeNetworkRequest() {
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 37420bf..89c79d3 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -80,7 +80,7 @@
DeviceNotAvailableException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
- false /* reinstall */, true /* grantPermissions */));
+ false /* reinstall */, true /* grantPermissions */, "-t"));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)
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 0e25d5e..d026fe0 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -318,11 +318,23 @@
/**************************
* Restricted mode tests. *
**************************/
- public void testRestrictedMode_networkAccess() throws Exception {
+ public void testNetworkAccess_restrictedMode() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
"testNetworkAccess");
}
+ /************************
+ * Expedited job tests. *
+ ************************/
+
+ public void testMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
+ }
+
+ public void testNonMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 4a1020d..25596a9 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -18,7 +18,10 @@
java_defaults {
name: "CtsNetTestCasesDefaults",
- defaults: ["cts_defaults"],
+ defaults: [
+ "cts_defaults",
+ "framework-connectivity-test-defaults",
+ ],
// Include both the 32 and 64 bit versions
compile_multilib: "both",
@@ -39,10 +42,6 @@
"src/**/*.java",
"src/**/*.kt",
],
- exclude_srcs: [
- // TODO: delete this after mainline-prod snaps to sc-dev.
- "src/android/net/cts/NetworkAgentTest.kt",
- ],
jarjar_rules: "jarjar-rules-shared.txt",
static_libs: [
"bouncycastle-unbundled",
@@ -68,7 +67,6 @@
// devices.
android_test {
name: "CtsNetTestCases",
- enabled: false, // Disabled in mainline-prod
defaults: ["CtsNetTestCasesDefaults"],
// TODO: CTS should not depend on the entirety of the networkstack code.
static_libs: [
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 78a01e2..474eefe 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index ef529f8..acf7d26 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -547,6 +547,7 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
fun testSetUnderlyingNetworksAndVpnSpecifier() {
+ val mySessionId = "MySession12345"
val request = NetworkRequest.Builder()
.addTransportType(TRANSPORT_TEST)
.addTransportType(TRANSPORT_VPN)
@@ -560,7 +561,7 @@
addTransportType(TRANSPORT_TEST)
addTransportType(TRANSPORT_VPN)
removeCapability(NET_CAPABILITY_NOT_VPN)
- setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE))
+ setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, mySessionId))
if (SdkLevel.isAtLeastS()) {
addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
}
@@ -580,6 +581,8 @@
assertNotNull(vpnNc)
assertEquals(VpnManager.TYPE_VPN_SERVICE,
(vpnNc.transportInfo as VpnTransportInfo).type)
+ // TODO: b/183938194 please fix the issue and enable following check.
+ // assertEquals(mySessionId, (vpnNc.transportInfo as VpnTransportInfo).sessionId)
val testAndVpn = intArrayOf(TRANSPORT_TEST, TRANSPORT_VPN)
assertTrue(hasAllTransports(vpnNc, testAndVpn))