Merge "Close the socket fd in case of an exception during setupSocket method to avoid socket leak." into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 83619d6..39009cb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,15 @@
+[Builtin Hooks]
+bpfmt = true
+clang_format = true
+ktfmt = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,hpp
+ktfmt = --kotlinlang-style
+
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 4cf93a8..bcf5e8b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"captiveportal-networkstack-resolve-tethering-mainline-presubmit": [
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -89,7 +89,7 @@
// Test with APK modules only, in cases where APEX is not supported, or the other modules
// were simply not updated
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -115,7 +115,7 @@
// really exist in the field, but there is no strong guarantee, and it is required by MTS
// testing for module qualification, where modules are tested independently.
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -158,8 +158,6 @@
},
// Run in addition to mainline-presubmit as mainline-presubmit is not
// supported in every branch.
- // CtsNetTestCasesLatestSdk uses stable API shims, so does not exercise
- // some latest APIs. Run CtsNetTestCases to get coverage of newer APIs.
{
"name": "CtsNetTestCases",
"options": [
@@ -171,18 +169,6 @@
}
]
},
- // Also run CtsNetTestCasesLatestSdk to ensure tests using older shims pass.
- {
- "name": "CtsNetTestCasesLatestSdk",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.RequiresDevice"
- }
- ]
- },
// CTS tests that target older SDKs.
{
"name": "CtsNetTestCasesMaxTargetSdk30",
@@ -267,11 +253,15 @@
},
{
"name": "FrameworksNetTests"
+ },
+ // TODO: Move to presumit after meet SLO requirement.
+ {
+ "name": "NetworkStaticLibHostPythonTests"
}
],
"mainline-presubmit": [
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -329,7 +319,7 @@
// Test with APK modules only, in cases where APEX is not supported, or the other modules
// were simply not updated
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -353,7 +343,7 @@
// really exist in the field, but there is no strong guarantee, and it is required by MTS
// testing for module qualification, where modules are tested independently.
{
- "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -404,7 +394,7 @@
"mainline-postsubmit": [
// Tests on physical devices with SIM cards: postsubmit only for capacity constraints
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"keywords": ["sim"]
},
{
@@ -418,7 +408,7 @@
},
// Postsubmit on virtual devices to monitor flakiness of @SkipMainlinePresubmit methods
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "androidx.test.filters.RequiresDevice"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index e84573b..b4426a6 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -69,13 +69,7 @@
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.tetheroffload.control-V1.1-java",
"android.hidl.manager-V1.2-java",
- "net-utils-framework-common",
- "net-utils-device-common",
- "net-utils-device-common-bpf",
- "net-utils-device-common-ip",
- "net-utils-device-common-netlink",
- "net-utils-device-common-struct",
- "net-utils-device-common-struct-base",
+ "net-utils-tethering",
"netd-client",
"tetheringstatsprotos",
],
diff --git a/Tethering/common/TetheringLib/lint-baseline.xml b/Tethering/common/TetheringLib/lint-baseline.xml
index ed5fbb0..5171efb 100644
--- a/Tethering/common/TetheringLib/lint-baseline.xml
+++ b/Tethering/common/TetheringLib/lint-baseline.xml
@@ -12,4 +12,15 @@
column="50"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `TetheringRequest()` is a flagged API and should be inside an `if (Flags.tetheringRequestWithSoftApConfig())` check (or annotate the surrounding method `build` with `@FlaggedApi(Flags.TETHERING_REQUEST_WITH_SOFT_AP_CONFIG) to transfer requirement to caller`)"
+ errorLine1=" return new TetheringRequest(mBuilderParcel);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Connectivity/Tethering/common/TetheringLib/src/android/net/TetheringManager.java"
+ line="814"
+ column="24"/>
+ </issue>
+
</issues>
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 2963f87..0f5a014 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -198,6 +198,10 @@
/**
* VIRTUAL tethering type.
+ *
+ * This tethering type is for providing external network to virtual machines
+ * running on top of Android devices, which are created and managed by
+ * AVF(Android Virtualization Framework).
* @hide
*/
@FlaggedApi(Flags.TETHERING_REQUEST_VIRTUAL)
@@ -698,7 +702,11 @@
/** A configuration set for TetheringRequest. */
private final TetheringRequestParcel mRequestParcel;
- private TetheringRequest(@NonNull final TetheringRequestParcel request) {
+ /**
+ * @hide
+ */
+ @FlaggedApi(Flags.TETHERING_REQUEST_WITH_SOFT_AP_CONFIG)
+ public TetheringRequest(@NonNull final TetheringRequestParcel request) {
mRequestParcel = request;
}
@@ -901,6 +909,28 @@
+ mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
+ mRequestParcel.showProvisioningUi + " ]";
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof TetheringRequest otherRequest)) return false;
+ TetheringRequestParcel parcel = getParcel();
+ TetheringRequestParcel otherParcel = otherRequest.getParcel();
+ return parcel.tetheringType == otherParcel.tetheringType
+ && Objects.equals(parcel.localIPv4Address, otherParcel.localIPv4Address)
+ && Objects.equals(parcel.staticClientAddress, otherParcel.staticClientAddress)
+ && parcel.exemptFromEntitlementCheck == otherParcel.exemptFromEntitlementCheck
+ && parcel.showProvisioningUi == otherParcel.showProvisioningUi
+ && parcel.connectivityScope == otherParcel.connectivityScope;
+ }
+
+ @Override
+ public int hashCode() {
+ TetheringRequestParcel parcel = getParcel();
+ return Objects.hash(parcel.tetheringType, parcel.localIPv4Address,
+ parcel.staticClientAddress, parcel.exemptFromEntitlementCheck,
+ parcel.showProvisioningUi, parcel.connectivityScope);
+ }
}
/**
@@ -1353,6 +1383,9 @@
@RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
public void registerTetheringEventCallback(@NonNull Executor executor,
@NonNull TetheringEventCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg);
@@ -1507,6 +1540,8 @@
Manifest.permission.ACCESS_NETWORK_STATE
})
public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) {
+ Objects.requireNonNull(callback);
+
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index fe5a0c6..b807544 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -44,10 +44,9 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.RoutingCoordinatorManager;
import android.net.TetheredClient;
import android.net.TetheringManager;
-import android.net.TetheringRequestParcel;
+import android.net.TetheringManager.TetheringRequest;
import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -72,7 +71,7 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetdUtils;
-import com.android.net.module.util.SdkUtil.LateSdk;
+import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.SyncStateMachine.StateInfo;
import com.android.net.module.util.ip.InterfaceController;
@@ -239,11 +238,8 @@
private final INetd mNetd;
@NonNull
private final BpfCoordinator mBpfCoordinator;
- // Contains null if the connectivity module is unsupported, as the routing coordinator is not
- // available. Must use LateSdk because MessageUtils enumerates fields in this class, so it
- // must be able to find all classes at runtime.
@NonNull
- private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinator;
+ private final RoutingCoordinatorManager mRoutingCoordinator;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
@@ -301,7 +297,7 @@
public IpServer(
String ifaceName, Handler handler, int interfaceType, SharedLog log,
INetd netd, @NonNull BpfCoordinator bpfCoordinator,
- @Nullable LateSdk<RoutingCoordinatorManager> routingCoordinator, Callback callback,
+ RoutingCoordinatorManager routingCoordinatorManager, Callback callback,
TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
TetheringMetrics tetheringMetrics, Dependencies deps) {
super(ifaceName, USE_SYNC_SM ? null : handler.getLooper());
@@ -309,7 +305,7 @@
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
mBpfCoordinator = bpfCoordinator;
- mRoutingCoordinator = routingCoordinator;
+ mRoutingCoordinator = routingCoordinatorManager;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -404,7 +400,7 @@
}
/** Enable this IpServer. IpServer state machine will be tethered or localHotspot state. */
- public void enable(final int requestedState, final TetheringRequestParcel request) {
+ public void enable(final int requestedState, final TetheringRequest request) {
sendMessage(CMD_TETHER_REQUESTED, requestedState, 0, request);
}
@@ -825,47 +821,25 @@
private void addInterfaceToNetwork(final int netId, @NonNull final String ifaceName) {
try {
- if (SdkLevel.isAtLeastS() && null != mRoutingCoordinator.value) {
- // TODO : remove this call in favor of using the LocalNetworkConfiguration
- // correctly, which will let ConnectivityService do it automatically.
- mRoutingCoordinator.value.addInterfaceToNetwork(netId, ifaceName);
- } else {
- mNetd.networkAddInterface(netId, ifaceName);
- }
- } catch (ServiceSpecificException | RemoteException e) {
+ // TODO : remove this call in favor of using the LocalNetworkConfiguration
+ // correctly, which will let ConnectivityService do it automatically.
+ mRoutingCoordinator.addInterfaceToNetwork(netId, ifaceName);
+ } catch (ServiceSpecificException e) {
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
}
}
- private void addInterfaceForward(@NonNull final String fromIface,
- @NonNull final String toIface) throws ServiceSpecificException, RemoteException {
- if (SdkLevel.isAtLeastS() && null != mRoutingCoordinator.value) {
- mRoutingCoordinator.value.addInterfaceForward(fromIface, toIface);
- } else {
- mNetd.tetherAddForward(fromIface, toIface);
- mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
- }
+ private void addInterfaceForward(@NonNull final String fromIface, @NonNull final String toIface)
+ throws ServiceSpecificException {
+ mRoutingCoordinator.addInterfaceForward(fromIface, toIface);
}
private void removeInterfaceForward(@NonNull final String fromIface,
@NonNull final String toIface) {
- if (SdkLevel.isAtLeastS() && null != mRoutingCoordinator.value) {
- try {
- mRoutingCoordinator.value.removeInterfaceForward(fromIface, toIface);
- } catch (ServiceSpecificException e) {
- mLog.e("Exception in removeInterfaceForward", e);
- }
- } else {
- try {
- mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in ipfwdRemoveInterfaceForward", e);
- }
- try {
- mNetd.tetherRemoveForward(fromIface, toIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in disableNat", e);
- }
+ try {
+ mRoutingCoordinator.removeInterfaceForward(fromIface, toIface);
+ } catch (RuntimeException e) {
+ mLog.e("Exception in removeInterfaceForward", e);
}
}
@@ -1006,18 +980,18 @@
mLinkProperties.setInterfaceName(mIfaceName);
}
- private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
+ private void maybeConfigureStaticIp(final TetheringRequest request) {
// Ignore static address configuration if they are invalid or null. In theory, static
// addresses should not be invalid here because TetheringManager do not allow caller to
// specify invalid static address configuration.
- if (request == null || request.localIPv4Address == null
- || request.staticClientAddress == null || !checkStaticAddressConfiguration(
- request.localIPv4Address, request.staticClientAddress)) {
+ if (request == null || request.getLocalIpv4Address() == null
+ || request.getClientStaticIpv4Address() == null || !checkStaticAddressConfiguration(
+ request.getLocalIpv4Address(), request.getClientStaticIpv4Address())) {
return;
}
- mStaticIpv4ServerAddr = request.localIPv4Address;
- mStaticIpv4ClientAddr = request.staticClientAddress;
+ mStaticIpv4ServerAddr = request.getLocalIpv4Address();
+ mStaticIpv4ClientAddr = request.getClientStaticIpv4Address();
}
class InitialState extends State {
@@ -1034,11 +1008,11 @@
mLastError = TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
case STATE_LOCAL_ONLY:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
+ maybeConfigureStaticIp((TetheringRequest) message.obj);
transitionTo(mLocalHotspotState);
break;
case STATE_TETHERED:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
+ maybeConfigureStaticIp((TetheringRequest) message.obj);
transitionTo(mTetheredState);
break;
default:
@@ -1370,7 +1344,7 @@
mBpfCoordinator.maybeAttachProgram(mIfaceName, ifname);
try {
addInterfaceForward(mIfaceName, ifname);
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RuntimeException e) {
mLog.e("Exception enabling iface forward", e);
cleanupUpstream();
mLastError = TETHER_ERROR_ENABLE_FORWARDING_ERROR;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0ff89d3..d62f18f 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -91,14 +91,12 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
-import android.net.RoutingCoordinatorManager;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringInterface;
import android.net.TetheringManager.TetheringRequest;
-import android.net.TetheringRequestParcel;
import android.net.Uri;
import android.net.ip.IpServer;
import android.net.wifi.WifiClient;
@@ -139,7 +137,7 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.NetdUtils;
-import com.android.net.module.util.SdkUtil.LateSdk;
+import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
@@ -148,7 +146,6 @@
import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
-import com.android.networkstack.tethering.util.TetheringUtils;
import com.android.networkstack.tethering.util.VersionedBroadcastListener;
import com.android.networkstack.tethering.wear.WearableConnectionManager;
@@ -232,7 +229,7 @@
// Currently active tethering requests per tethering type. Only one of each type can be
// requested at a time. After a tethering type is requested, the map keeps tethering parameters
// to be used after the interface comes up asynchronously.
- private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
+ private final SparseArray<TetheringRequest> mActiveTetheringRequests =
new SparseArray<>();
private final Context mContext;
@@ -248,10 +245,7 @@
private final Handler mHandler;
private final INetd mNetd;
private final NetdCallback mNetdCallback;
- // Contains null if the connectivity module is unsupported, as the routing coordinator is not
- // available. Must use LateSdk because MessageUtils enumerates fields in this class, so it
- // must be able to find all classes at runtime.
- @NonNull private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinator;
+ private final RoutingCoordinatorManager mRoutingCoordinator;
private final UserRestrictionActionListener mTetheringRestriction;
private final ActiveDataSubIdListener mActiveDataSubIdListener;
private final ConnectedClientsTracker mConnectedClientsTracker;
@@ -296,11 +290,11 @@
mLog.mark("Tethering.constructed");
mDeps = deps;
mContext = mDeps.getContext();
- mNetd = mDeps.getINetd(mContext);
- mRoutingCoordinator = mDeps.getRoutingCoordinator(mContext);
+ mNetd = mDeps.getINetd(mContext, mLog);
+ mRoutingCoordinator = mDeps.getRoutingCoordinator(mContext, mLog);
mLooper = mDeps.makeTetheringLooper();
mNotificationUpdater = mDeps.makeNotificationUpdater(mContext, mLooper);
- mTetheringMetrics = mDeps.makeTetheringMetrics();
+ mTetheringMetrics = mDeps.makeTetheringMetrics(mContext);
// This is intended to ensrure that if something calls startTethering(bluetooth) just after
// bluetooth is enabled. Before onServiceConnected is called, store the calls into this
@@ -661,28 +655,27 @@
processInterfaceStateChange(iface, false /* enabled */);
}
- void startTethering(final TetheringRequestParcel request, final String callerPkg,
+ void startTethering(final TetheringRequest request, final String callerPkg,
final IIntResultListener listener) {
mHandler.post(() -> {
- final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
- request.tetheringType);
+ final int type = request.getTetheringType();
+ final TetheringRequest unfinishedRequest = mActiveTetheringRequests.get(type);
// If tethering is already enabled with a different request,
// disable before re-enabling.
- if (unfinishedRequest != null
- && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {
- enableTetheringInternal(request.tetheringType, false /* disabled */, null);
- mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);
+ if (unfinishedRequest != null && !unfinishedRequest.equals(request)) {
+ enableTetheringInternal(type, false /* disabled */, null);
+ mEntitlementMgr.stopProvisioningIfNeeded(type);
}
- mActiveTetheringRequests.put(request.tetheringType, request);
+ mActiveTetheringRequests.put(type, request);
- if (request.exemptFromEntitlementCheck) {
- mEntitlementMgr.setExemptedDownstreamType(request.tetheringType);
+ if (request.isExemptFromEntitlementCheck()) {
+ mEntitlementMgr.setExemptedDownstreamType(type);
} else {
- mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
- request.showProvisioningUi);
+ mEntitlementMgr.startProvisioningIfNeeded(type,
+ request.getShouldShowEntitlementUi());
}
- enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
- mTetheringMetrics.createBuilder(request.tetheringType, callerPkg);
+ enableTetheringInternal(type, true /* enabled */, listener);
+ mTetheringMetrics.createBuilder(type, callerPkg);
});
}
@@ -1018,7 +1011,7 @@
//
// This code cannot race with untether() because they both run on the handler thread.
final int type = tetherState.ipServer.interfaceType();
- final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
+ final TetheringRequest request = mActiveTetheringRequests.get(type, null);
if (request != null) {
mActiveTetheringRequests.delete(type);
}
@@ -1075,14 +1068,14 @@
}
private int getRequestedState(int type) {
- final TetheringRequestParcel request = mActiveTetheringRequests.get(type);
+ final TetheringRequest request = mActiveTetheringRequests.get(type);
// The request could have been deleted before we had a chance to complete it.
// If so, assume that the scope is the default scope for this tethering type.
// This likely doesn't matter - if the request has been deleted, then tethering is
// likely going to be stopped soon anyway.
final int connectivityScope = (request != null)
- ? request.connectivityScope
+ ? request.getConnectivityScope()
: TetheringRequest.getDefaultConnectivityScope(type);
return connectivityScope == CONNECTIVITY_SCOPE_LOCAL
@@ -1381,7 +1374,7 @@
}
@VisibleForTesting
- SparseArray<TetheringRequestParcel> getActiveTetheringRequests() {
+ SparseArray<TetheringRequest> getActiveTetheringRequests() {
return mActiveTetheringRequests;
}
@@ -2400,9 +2393,6 @@
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();
@@ -2440,9 +2430,6 @@
/** Unregister tethering event callback */
void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
- if (callback == null) {
- throw new NullPointerException();
- }
mHandler.post(() -> {
mTetheringEventCallbacks.unregister(callback);
});
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 3f86056..5d9d349 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -22,7 +22,6 @@
import android.bluetooth.BluetoothPan;
import android.content.Context;
import android.net.INetd;
-import android.net.RoutingCoordinatorManager;
import android.net.connectivity.ConnectivityInternalApiUtil;
import android.net.ip.IpServer;
import android.os.Build;
@@ -36,7 +35,8 @@
import androidx.annotation.RequiresApi;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.SdkUtil.LateSdk;
+import com.android.net.module.util.RoutingCoordinatorManager;
+import com.android.net.module.util.RoutingCoordinatorService;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.BluetoothPanShimImpl;
import com.android.networkstack.apishim.common.BluetoothPanShim;
@@ -120,19 +120,26 @@
/**
* Get a reference to INetd to be used by tethering.
*/
- public INetd getINetd(Context context) {
- return INetd.Stub.asInterface(
- (IBinder) context.getSystemService(Context.NETD_SERVICE));
+ public INetd getINetd(Context context, SharedLog log) {
+ final INetd netd =
+ INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
+ if (netd == null) {
+ log.wtf("INetd is null");
+ }
+ return netd;
}
/**
- * Get the routing coordinator, or null if below S.
+ * Get the routing coordinator.
*/
- @Nullable
- public LateSdk<RoutingCoordinatorManager> getRoutingCoordinator(Context context) {
- if (!SdkLevel.isAtLeastS()) return new LateSdk<>(null);
- return new LateSdk<>(
- ConnectivityInternalApiUtil.getRoutingCoordinatorManager(context));
+ public RoutingCoordinatorManager getRoutingCoordinator(Context context, SharedLog log) {
+ IBinder binder;
+ if (!SdkLevel.isAtLeastS()) {
+ binder = new RoutingCoordinatorService(getINetd(context, log));
+ } else {
+ binder = ConnectivityInternalApiUtil.getRoutingCoordinator(context);
+ }
+ return new RoutingCoordinatorManager(context, binder);
}
/**
@@ -186,8 +193,8 @@
/**
* Make the TetheringMetrics to be used by tethering.
*/
- public TetheringMetrics makeTetheringMetrics() {
- return new TetheringMetrics();
+ public TetheringMetrics makeTetheringMetrics(Context ctx) {
+ return new TetheringMetrics(ctx);
}
/**
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 623f502..454cbf1 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -38,6 +38,7 @@
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
import android.net.NetworkStack;
+import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -137,8 +138,8 @@
listener)) {
return;
}
-
- mTethering.startTethering(request, callerPkg, listener);
+ // TODO(b/216524590): Add UID/packageName of caller to TetheringRequest here
+ mTethering.startTethering(new TetheringRequest(request), callerPkg, listener);
}
@Override
@@ -163,6 +164,8 @@
@Override
public void registerTetheringEventCallback(ITetheringEventCallback callback,
String callerPkg) {
+ // Silently ignore call if the callback is null. This can only happen via reflection.
+ if (callback == null) return;
try {
if (!hasTetherAccessPermission()) {
callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
@@ -175,6 +178,8 @@
@Override
public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
String callerPkg) {
+ // Silently ignore call if the callback is null. This can only happen via reflection.
+ if (callback == null) return;
try {
if (!hasTetherAccessPermission()) {
callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index 814afcd..136dfb1 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -46,6 +46,7 @@
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import android.annotation.Nullable;
+import android.content.Context;
import android.net.NetworkCapabilities;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
@@ -81,16 +82,50 @@
private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
+ private final Context mContext;
+ private final Dependencies mDependencies;
private UpstreamType mCurrentUpstream = null;
private Long mCurrentUpStreamStartTime = 0L;
+ /**
+ * Dependencies of TetheringMetrics, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * @see TetheringStatsLog
+ */
+ public void write(NetworkTetheringReported reported) {
+ TetheringStatsLog.write(
+ TetheringStatsLog.NETWORK_TETHERING_REPORTED,
+ reported.getErrorCode().getNumber(),
+ reported.getDownstreamType().getNumber(),
+ reported.getUpstreamType().getNumber(),
+ reported.getUserType().getNumber(),
+ reported.getUpstreamEvents().toByteArray(),
+ reported.getDurationMillis());
+ }
+
+ /**
+ * @see System#currentTimeMillis()
+ */
+ public long timeNow() {
+ return System.currentTimeMillis();
+ }
+ }
/**
- * Return the current system time in milliseconds.
- * @return the current system time in milliseconds.
+ * Constructor for the TetheringMetrics class.
+ *
+ * @param context The Context object used to access system services.
*/
- public long timeNow() {
- return System.currentTimeMillis();
+ public TetheringMetrics(Context context) {
+ this(context, new Dependencies());
+ }
+
+ TetheringMetrics(Context context, Dependencies dependencies) {
+ mContext = context;
+ mDependencies = dependencies;
}
private static class RecordUpstreamEvent {
@@ -123,7 +158,7 @@
.setUpstreamEvents(UpstreamEvents.newBuilder())
.setDurationMillis(0);
mBuilderMap.put(downstreamType, statsBuilder);
- mDownstreamStartTime.put(downstreamType, timeNow());
+ mDownstreamStartTime.put(downstreamType, mDependencies.timeNow());
}
/**
@@ -149,7 +184,7 @@
UpstreamType upstream = transportTypeToUpstreamTypeEnum(ns);
if (upstream.equals(mCurrentUpstream)) return;
- final long newTime = timeNow();
+ final long newTime = mDependencies.timeNow();
if (mCurrentUpstream != null) {
mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
mCurrentUpstream));
@@ -206,7 +241,7 @@
event.mUpstreamType, 0L /* txBytes */, 0L /* rxBytes */);
}
final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
- final long stopTime = timeNow();
+ final long stopTime = mDependencies.timeNow();
// Handle the last upstream event.
addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream,
0L /* txBytes */, 0L /* rxBytes */);
@@ -248,15 +283,7 @@
@VisibleForTesting
public void write(@NonNull final NetworkTetheringReported reported) {
final byte[] upstreamEvents = reported.getUpstreamEvents().toByteArray();
-
- TetheringStatsLog.write(
- TetheringStatsLog.NETWORK_TETHERING_REPORTED,
- reported.getErrorCode().getNumber(),
- reported.getDownstreamType().getNumber(),
- reported.getUpstreamType().getNumber(),
- reported.getUserType().getNumber(),
- upstreamEvents,
- reported.getDurationMillis());
+ mDependencies.write(reported);
if (DBG) {
Log.d(
TAG,
@@ -374,4 +401,22 @@
return UpstreamType.UT_UNKNOWN;
}
+
+ /**
+ * Check whether tethering metrics' data usage can be collected for a given upstream type.
+ *
+ * @param type the upstream type
+ */
+ public static boolean isUsageSupportedForUpstreamType(@NonNull UpstreamType type) {
+ switch(type) {
+ case UT_CELLULAR:
+ case UT_WIFI:
+ case UT_BLUETOOTH:
+ case UT_ETHERNET:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
index e6236df..76c2f0d 100644
--- a/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
@@ -16,7 +16,6 @@
package com.android.networkstack.tethering.util;
import android.net.TetherStatsParcel;
-import android.net.TetheringRequestParcel;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -29,7 +28,6 @@
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
-import java.util.Objects;
/**
* The classes and the methods for tethering utilization.
@@ -158,20 +156,6 @@
return s & 0xffff;
}
- /** Check whether two TetheringRequestParcels are the same. */
- public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
- final TetheringRequestParcel otherRequest) {
- if (request == otherRequest) return true;
-
- return request != null && otherRequest != null
- && request.tetheringType == otherRequest.tetheringType
- && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
- && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
- && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
- && request.showProvisioningUi == otherRequest.showProvisioningUi
- && request.connectivityScope == otherRequest.connectivityScope;
- }
-
/** Get inet6 address for all nodes given scope ID. */
public static Inet6Address getAllNodesForScopeId(int scopeId) {
try {
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 5c258b2..9cdba2f 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.Manifest.permission.DUMP;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
@@ -26,36 +27,51 @@
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
import static android.system.OsConstants.ICMP_ECHO;
import static android.system.OsConstants.ICMP_ECHOREPLY;
+import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
import static com.android.net.module.util.HexDump.dumpHexString;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static com.android.testutils.DeviceInfoUtils.KVersion;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.content.Context;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringTester.TetheredDevice;
import android.os.Build;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.BpfDump;
import com.android.net.module.util.Ipv6Utils;
import com.android.net.module.util.Struct;
+import com.android.net.module.util.bpf.Tether4Key;
+import com.android.net.module.util.bpf.Tether4Value;
+import com.android.net.module.util.bpf.TetherStatsKey;
+import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.UdpHeader;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DeviceInfoUtils;
+import com.android.testutils.DumpTestUtils;
import com.android.testutils.NetworkStackModuleTest;
import com.android.testutils.TapPacketReader;
@@ -73,7 +89,9 @@
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeoutException;
@@ -89,6 +107,26 @@
private static final short ICMPECHO_ID = 0x0;
private static final short ICMPECHO_SEQ = 0x0;
+ private static final int DUMP_POLLING_MAX_RETRY = 100;
+ private static final int DUMP_POLLING_INTERVAL_MS = 50;
+ // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
+ // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
+ private static final int UDP_STREAM_TS_MS = 2000;
+ // Give slack time for waiting UDP stream mode because handling conntrack event in user space
+ // may not in precise time. Used to reduce the flaky rate.
+ private static final int UDP_STREAM_SLACK_MS = 500;
+ // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int RX_UDP_PACKET_SIZE = 30;
+ private static final int RX_UDP_PACKET_COUNT = 456;
+ // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int TX_UDP_PACKET_SIZE = 30;
+ private static final int TX_UDP_PACKET_COUNT = 123;
+
+ private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
+ private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
+ private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
+ private static final String LINE_DELIMITER = "\\n";
+
// TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports
// building packet for given arguments.
private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] {
@@ -802,4 +840,217 @@
final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66");
assertTrue(tester.testDhcpServerAlive(macAddress));
}
+
+ private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
+ final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
+ return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
+ || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
+ || current.isAtLeast(new KVersion(5, 4, 98));
+ }
+
+ @Test
+ public void testIsUdpOffloadSupportedByKernel() throws Exception {
+ assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
+ assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
+
+ assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
+ assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
+
+ assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
+ }
+
+ private static void assumeKernelSupportBpfOffloadUdpV4() {
+ final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
+ assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
+ isUdpOffloadSupportedByKernel(kernelVersion));
+ }
+
+ @Test
+ public void testKernelSupportBpfOffloadUdpV4() throws Exception {
+ assumeKernelSupportBpfOffloadUdpV4();
+ }
+
+ private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
+ final String dumpStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
+
+ // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
+ // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
+ // RRO to override the enabled default value. Get the tethering config via dumpsys.
+ // $ dumpsys tethering
+ // mIsBpfEnabled: true
+ boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
+ if (!enabled) {
+ Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
+ }
+ return enabled;
+ }
+
+ @Test
+ public void testTetherConfigBpfOffloadEnabled() throws Exception {
+ assumeTrue(isTetherConfigBpfOffloadEnabled());
+ }
+
+ @NonNull
+ private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
+ final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
+ final String rawMapStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
+ final HashMap<K, V> map = new HashMap<>();
+
+ for (final String line : rawMapStr.split(LINE_DELIMITER)) {
+ final Pair<K, V> rule =
+ BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
+ map.put(rule.first, rule.second);
+ }
+ return map;
+ }
+
+ @Nullable
+ private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
+ for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
+ final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
+ if (!map.isEmpty()) return map;
+
+ Thread.sleep(DUMP_POLLING_INTERVAL_MS);
+ }
+
+ fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
+ return null;
+ }
+
+ // Test network topology:
+ //
+ // public network (rawip) private network
+ // | UE |
+ // +------------+ V +------------+------------+ V +------------+
+ // | Sever +---------+ Upstream | Downstream +---------+ Client |
+ // +------------+ +------------+------------+ +------------+
+ // remote ip public ip private ip
+ // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876
+ //
+ private void runUdp4Test() throws Exception {
+ final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+ toList(TEST_IP4_DNS));
+ final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+ // TODO: remove the connectivity verification for upstream connected notification race.
+ // Because async upstream connected notification can't guarantee the tethering routing is
+ // ready to use. Need to test tethering connectivity before testing.
+ // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
+ // from upstream. That can guarantee that the routing is ready. Long term plan is that
+ // refactors upstream connected notification from async to sync.
+ probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+ final MacAddress srcMac = tethered.macAddr;
+ final MacAddress dstMac = tethered.routerMacAddr;
+ final InetAddress remoteIp = REMOTE_IP4_ADDR;
+ final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
+ final InetAddress clientIp = tethered.ipv4Addr;
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+ sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+
+ // Send second UDP packet in original direction.
+ // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
+ // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
+ // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
+ // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
+ // and apply ASSURED flag.
+ // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
+ // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
+ Thread.sleep(UDP_STREAM_TS_MS);
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+
+ // Give a slack time for handling conntrack event in user space.
+ Thread.sleep(UDP_STREAM_SLACK_MS);
+
+ // [1] Verify IPv4 upstream rule map.
+ final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
+ Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
+ assertNotNull(upstreamMap);
+ assertEquals(1, upstreamMap.size());
+
+ final Map.Entry<Tether4Key, Tether4Value> rule =
+ upstreamMap.entrySet().iterator().next();
+
+ final Tether4Key upstream4Key = rule.getKey();
+ assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
+ assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
+ assertEquals(LOCAL_PORT, upstream4Key.srcPort);
+ assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
+ assertEquals(REMOTE_PORT, upstream4Key.dstPort);
+
+ final Tether4Value upstream4Value = rule.getValue();
+ assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
+ InetAddress.getByAddress(upstream4Value.src46).getAddress()));
+ assertEquals(LOCAL_PORT, upstream4Value.srcPort);
+ assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
+ InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
+ assertEquals(REMOTE_PORT, upstream4Value.dstPort);
+
+ // [2] Verify stats map.
+ // Transmit packets on both direction for verifying stats. Because we only care the
+ // packet count in stats test, we just reuse the existing packets to increaes
+ // the packet count on both direction.
+
+ // Send packets on original direction.
+ for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
+ false /* is4To6 */);
+ }
+
+ // Send packets on reply direction.
+ for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
+ sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+ }
+
+ // Dump stats map to verify.
+ final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
+ TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
+ assertNotNull(statsMap);
+ assertEquals(1, statsMap.size());
+
+ final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
+ statsMap.entrySet().iterator().next();
+
+ // TODO: verify the upstream index in TetherStatsKey.
+
+ final TetherStatsValue statsValue = stats.getValue();
+ assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
+ assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
+ assertEquals(0, statsValue.rxErrors);
+ assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
+ assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
+ assertEquals(0, statsValue.txErrors);
+ }
+
+ /**
+ * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
+ * Minimum test requirement:
+ * 1. S+ device.
+ * 2. Tethering config enables tethering BPF offload.
+ * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
+ *
+ * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
+ */
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testTetherBpfOffloadUdpV4() throws Exception {
+ assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
+ assumeKernelSupportBpfOffloadUdpV4();
+
+ runUdp4Test();
+ }
}
diff --git a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java b/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
deleted file mode 100644
index c2bc812..0000000
--- a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import static android.Manifest.permission.DUMP;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import static com.android.testutils.DeviceInfoUtils.KVersion;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.content.Context;
-import android.net.TetheringTester.TetheredDevice;
-import android.os.Build;
-import android.os.VintfRuntimeInfo;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.net.module.util.BpfDump;
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.bpf.Tether4Key;
-import com.android.net.module.util.bpf.Tether4Value;
-import com.android.net.module.util.bpf.TetherStatsKey;
-import com.android.net.module.util.bpf.TetherStatsValue;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.DeviceInfoUtils;
-import com.android.testutils.DumpTestUtils;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class MtsEthernetTetheringTest extends EthernetTetheringTestBase {
- @Rule
- public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
-
- private static final String TAG = MtsEthernetTetheringTest.class.getSimpleName();
-
- private static final int DUMP_POLLING_MAX_RETRY = 100;
- private static final int DUMP_POLLING_INTERVAL_MS = 50;
- // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
- // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
- private static final int UDP_STREAM_TS_MS = 2000;
- // Give slack time for waiting UDP stream mode because handling conntrack event in user space
- // may not in precise time. Used to reduce the flaky rate.
- private static final int UDP_STREAM_SLACK_MS = 500;
- // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
- private static final int RX_UDP_PACKET_SIZE = 30;
- private static final int RX_UDP_PACKET_COUNT = 456;
- // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
- private static final int TX_UDP_PACKET_SIZE = 30;
- private static final int TX_UDP_PACKET_COUNT = 123;
-
- private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
- private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
- private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
- private static final String LINE_DELIMITER = "\\n";
-
- private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
- final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
- return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
- || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
- || current.isAtLeast(new KVersion(5, 4, 98));
- }
-
- @Test
- public void testIsUdpOffloadSupportedByKernel() throws Exception {
- assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
- assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
- assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
- assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
- assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
-
- assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
- assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
- assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
- assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
- assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
-
- assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
- assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
- assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
- }
-
- private static void assumeKernelSupportBpfOffloadUdpV4() {
- final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
- assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
- isUdpOffloadSupportedByKernel(kernelVersion));
- }
-
- @Test
- public void testKernelSupportBpfOffloadUdpV4() throws Exception {
- assumeKernelSupportBpfOffloadUdpV4();
- }
-
- private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
- final String dumpStr = runAsShell(DUMP, () ->
- DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
-
- // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
- // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
- // RRO to override the enabled default value. Get the tethering config via dumpsys.
- // $ dumpsys tethering
- // mIsBpfEnabled: true
- boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
- if (!enabled) {
- Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
- }
- return enabled;
- }
-
- @Test
- public void testTetherConfigBpfOffloadEnabled() throws Exception {
- assumeTrue(isTetherConfigBpfOffloadEnabled());
- }
-
- @NonNull
- private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
- Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
- throws Exception {
- final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
- final String rawMapStr = runAsShell(DUMP, () ->
- DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
- final HashMap<K, V> map = new HashMap<>();
-
- for (final String line : rawMapStr.split(LINE_DELIMITER)) {
- final Pair<K, V> rule =
- BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
- map.put(rule.first, rule.second);
- }
- return map;
- }
-
- @Nullable
- private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
- Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
- throws Exception {
- for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
- final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
- if (!map.isEmpty()) return map;
-
- Thread.sleep(DUMP_POLLING_INTERVAL_MS);
- }
-
- fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
- return null;
- }
-
- // Test network topology:
- //
- // public network (rawip) private network
- // | UE |
- // +------------+ V +------------+------------+ V +------------+
- // | Sever +---------+ Upstream | Downstream +---------+ Client |
- // +------------+ +------------+------------+ +------------+
- // remote ip public ip private ip
- // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876
- //
- private void runUdp4Test() throws Exception {
- final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
- toList(TEST_IP4_DNS));
- final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
-
- // TODO: remove the connectivity verification for upstream connected notification race.
- // Because async upstream connected notification can't guarantee the tethering routing is
- // ready to use. Need to test tethering connectivity before testing.
- // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
- // from upstream. That can guarantee that the routing is ready. Long term plan is that
- // refactors upstream connected notification from async to sync.
- probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
-
- final MacAddress srcMac = tethered.macAddr;
- final MacAddress dstMac = tethered.routerMacAddr;
- final InetAddress remoteIp = REMOTE_IP4_ADDR;
- final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
- final InetAddress clientIp = tethered.ipv4Addr;
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
- sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
-
- // Send second UDP packet in original direction.
- // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
- // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
- // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
- // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
- // and apply ASSURED flag.
- // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
- // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
- Thread.sleep(UDP_STREAM_TS_MS);
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
-
- // Give a slack time for handling conntrack event in user space.
- Thread.sleep(UDP_STREAM_SLACK_MS);
-
- // [1] Verify IPv4 upstream rule map.
- final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
- Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
- assertNotNull(upstreamMap);
- assertEquals(1, upstreamMap.size());
-
- final Map.Entry<Tether4Key, Tether4Value> rule =
- upstreamMap.entrySet().iterator().next();
-
- final Tether4Key upstream4Key = rule.getKey();
- assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
- assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
- assertEquals(LOCAL_PORT, upstream4Key.srcPort);
- assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
- assertEquals(REMOTE_PORT, upstream4Key.dstPort);
-
- final Tether4Value upstream4Value = rule.getValue();
- assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
- InetAddress.getByAddress(upstream4Value.src46).getAddress()));
- assertEquals(LOCAL_PORT, upstream4Value.srcPort);
- assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
- InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
- assertEquals(REMOTE_PORT, upstream4Value.dstPort);
-
- // [2] Verify stats map.
- // Transmit packets on both direction for verifying stats. Because we only care the
- // packet count in stats test, we just reuse the existing packets to increaes
- // the packet count on both direction.
-
- // Send packets on original direction.
- for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
- false /* is4To6 */);
- }
-
- // Send packets on reply direction.
- for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
- sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
- }
-
- // Dump stats map to verify.
- final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
- TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
- assertNotNull(statsMap);
- assertEquals(1, statsMap.size());
-
- final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
- statsMap.entrySet().iterator().next();
-
- // TODO: verify the upstream index in TetherStatsKey.
-
- final TetherStatsValue statsValue = stats.getValue();
- assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
- assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
- assertEquals(0, statsValue.rxErrors);
- assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
- assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
- assertEquals(0, statsValue.txErrors);
- }
-
- /**
- * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
- * Minimum test requirement:
- * 1. S+ device.
- * 2. Tethering config enables tethering BPF offload.
- * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
- *
- * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
- */
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.R)
- public void testTetherBpfOffloadUdpV4() throws Exception {
- assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
- assumeKernelSupportBpfOffloadUdpV4();
-
- runUdp4Test();
- }
-}
diff --git a/Tethering/tests/privileged/Android.bp b/Tethering/tests/privileged/Android.bp
index ba6be66..3597a91 100644
--- a/Tethering/tests/privileged/Android.bp
+++ b/Tethering/tests/privileged/Android.bp
@@ -53,4 +53,5 @@
"TetheringApiCurrentLib",
],
compile_multilib: "both",
+ min_sdk_version: "30",
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 00eb3b1..177296a 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -51,6 +51,7 @@
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
@@ -73,7 +74,6 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.RoutingCoordinatorManager;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpEventCallbacks;
@@ -91,6 +91,7 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.tethering.BpfCoordinator;
@@ -174,8 +175,7 @@
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpServer.Dependencies mDependencies;
@Mock private PrivateAddressCoordinator mAddressCoordinator;
- private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinatorManager =
- new LateSdk<>(SdkLevel.isAtLeastS() ? mock(RoutingCoordinatorManager.class) : null);
+ @Mock private RoutingCoordinatorManager mRoutingCoordinatorManager;
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private TetheringMetrics mTetheringMetrics;
@@ -280,24 +280,6 @@
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
- // Simulate the behavior of RoutingCoordinator
- if (null != mRoutingCoordinatorManager.value) {
- doAnswer(it -> {
- final String fromIface = (String) it.getArguments()[0];
- final String toIface = (String) it.getArguments()[1];
- mNetd.tetherAddForward(fromIface, toIface);
- mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
- return null;
- }).when(mRoutingCoordinatorManager.value).addInterfaceForward(any(), any());
- doAnswer(it -> {
- final String fromIface = (String) it.getArguments()[0];
- final String toIface = (String) it.getArguments()[1];
- mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
- mNetd.tetherRemoveForward(fromIface, toIface);
- return null;
- }).when(mRoutingCoordinatorManager.value).removeInterfaceForward(any(), any());
- }
-
setUpDhcpServer();
}
@@ -455,105 +437,114 @@
// Telling the state machine about its upstream interface triggers
// a little more configuration.
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder inOrder = inOrder(mNetd, mBpfCoordinator);
+ InOrder inOrder = inOrder(mBpfCoordinator, mRoutingCoordinatorManager);
// Add the forwarding pair <IFACE_NAME, UPSTREAM_IFACE>.
inOrder.verify(mBpfCoordinator).maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX,
UPSTREAM_IFACE);
inOrder.verify(mBpfCoordinator).maybeAttachProgram(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mRoutingCoordinatorManager).addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mCallback, mBpfCoordinator);
+ verifyNoMoreInteractions(mCallback, mBpfCoordinator, mRoutingCoordinatorManager);
}
@Test
public void handlesChangingUpstream() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
+ clearInvocations(mBpfCoordinator, mRoutingCoordinatorManager);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mBpfCoordinator);
+ InOrder inOrder = inOrder(mBpfCoordinator, mRoutingCoordinatorManager);
// Remove the forwarding pair <IFACE_NAME, UPSTREAM_IFACE>.
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
// Add the forwarding pair <IFACE_NAME, UPSTREAM_IFACE2>.
inOrder.verify(mBpfCoordinator).maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX2,
UPSTREAM_IFACE2);
inOrder.verify(mBpfCoordinator).maybeAttachProgram(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mRoutingCoordinatorManager).addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNetd, mCallback, mBpfCoordinator);
+ verifyNoMoreInteractions(mCallback, mBpfCoordinator, mRoutingCoordinatorManager);
}
@Test
public void handlesChangingUpstreamNatFailure() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
+ doThrow(RuntimeException.class)
+ .when(mRoutingCoordinatorManager)
+ .addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mBpfCoordinator);
+ InOrder inOrder = inOrder(mBpfCoordinator, mRoutingCoordinatorManager);
// Remove the forwarding pair <IFACE_NAME, UPSTREAM_IFACE>.
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
// Add the forwarding pair <IFACE_NAME, UPSTREAM_IFACE2> and expect that failed on
- // tetherAddForward.
+ // addInterfaceForward.
inOrder.verify(mBpfCoordinator).maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX2,
UPSTREAM_IFACE2);
inOrder.verify(mBpfCoordinator).maybeAttachProgram(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mRoutingCoordinatorManager).addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
// Remove the forwarding pair <IFACE_NAME, UPSTREAM_IFACE2> to fallback.
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@Test
public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward(
- IFACE_NAME, UPSTREAM_IFACE2);
+ doThrow(RuntimeException.class)
+ .when(mRoutingCoordinatorManager)
+ .addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mBpfCoordinator);
+ InOrder inOrder = inOrder(mBpfCoordinator, mRoutingCoordinatorManager);
// Remove the forwarding pair <IFACE_NAME, UPSTREAM_IFACE>.
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
// Add the forwarding pair <IFACE_NAME, UPSTREAM_IFACE2> and expect that failed on
// ipfwdAddInterfaceForward.
inOrder.verify(mBpfCoordinator).maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX2,
UPSTREAM_IFACE2);
inOrder.verify(mBpfCoordinator).maybeAttachProgram(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mRoutingCoordinatorManager).addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
// Remove the forwarding pair <IFACE_NAME, UPSTREAM_IFACE2> to fallback.
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@Test
public void canUnrequestTetheringWithUpstream() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
+ clearInvocations(
+ mNetd, mCallback, mAddressCoordinator, mBpfCoordinator, mRoutingCoordinatorManager);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator, mBpfCoordinator);
+ InOrder inOrder =
+ inOrder(
+ mNetd,
+ mCallback,
+ mAddressCoordinator,
+ mBpfCoordinator,
+ mRoutingCoordinatorManager);
inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
+ inOrder.verify(mRoutingCoordinatorManager)
+ .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mBpfCoordinator).updateIpv6UpstreamInterface(
mIpServer, NO_UPSTREAM, NO_PREFIXES);
// When tethering stops, upstream interface is set to zero and thus clearing all upstream
@@ -572,7 +563,8 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator, mBpfCoordinator);
+ verifyNoMoreInteractions(
+ mNetd, mCallback, mAddressCoordinator, mBpfCoordinator, mRoutingCoordinatorManager);
}
@Test
@@ -624,10 +616,14 @@
public void shouldTearDownUsbOnUpstreamError() throws Exception {
initTetheredStateMachine(TETHERING_USB, null);
- doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString());
+ doThrow(RuntimeException.class)
+ .when(mRoutingCoordinatorManager)
+ .addInterfaceForward(anyString(), anyString());
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
- usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
+ InOrder usbTeardownOrder = inOrder(mNetd, mCallback, mRoutingCoordinatorManager);
+ usbTeardownOrder
+ .verify(mRoutingCoordinatorManager)
+ .addInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index da81bda..c0d7ad4 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -45,6 +45,7 @@
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
import android.net.TetheringManager;
+import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.os.Bundle;
@@ -311,7 +312,8 @@
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
- verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
+ verify(mTethering).startTethering(
+ eq(new TetheringRequest(request)), eq(TEST_CALLER_PKG), eq(result));
}
@Test
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 9f430af..6ba5d48 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -142,7 +142,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.RouteInfo;
-import android.net.RoutingCoordinatorManager;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
import android.net.TetheredClient.AddressInfo;
@@ -150,7 +149,7 @@
import android.net.TetheringConfigurationParcel;
import android.net.TetheringInterface;
import android.net.TetheringManager;
-import android.net.TetheringRequestParcel;
+import android.net.TetheringManager.TetheringRequest;
import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -191,7 +190,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.InterfaceParams;
-import com.android.net.module.util.SdkUtil.LateSdk;
+import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.IpNeighborMonitor;
import com.android.networkstack.apishim.common.BluetoothPanShim;
@@ -292,6 +291,7 @@
@Mock private BluetoothPanShim mBluetoothPanShim;
@Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim;
@Mock private TetheringMetrics mTetheringMetrics;
+ @Mock private RoutingCoordinatorManager mRoutingCoordinatorManager;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -484,10 +484,10 @@
return mEntitleMgr;
}
- @Nullable
@Override
- public LateSdk<RoutingCoordinatorManager> getRoutingCoordinator(final Context context) {
- return new LateSdk<>(null);
+ public RoutingCoordinatorManager getRoutingCoordinator(final Context context,
+ SharedLog log) {
+ return mRoutingCoordinatorManager;
}
@Override
@@ -498,7 +498,7 @@
}
@Override
- public INetd getINetd(Context context) {
+ public INetd getINetd(Context context, SharedLog log) {
return mNetd;
}
@@ -528,7 +528,7 @@
}
@Override
- public TetheringMetrics makeTetheringMetrics() {
+ public TetheringMetrics makeTetheringMetrics(Context ctx) {
return mTetheringMetrics;
}
@@ -743,22 +743,21 @@
doReturn(upstreamState).when(mUpstreamNetworkMonitor).selectPreferredUpstreamType(any());
}
- private TetheringRequestParcel createTetheringRequestParcel(final int type) {
- return createTetheringRequestParcel(type, null, null, false, CONNECTIVITY_SCOPE_GLOBAL);
+ private TetheringRequest createTetheringRequest(final int type) {
+ return createTetheringRequest(type, null, null, false, CONNECTIVITY_SCOPE_GLOBAL);
}
- private TetheringRequestParcel createTetheringRequestParcel(final int type,
- final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt,
- final int scope) {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = type;
- request.localIPv4Address = serverAddr;
- request.staticClientAddress = clientAddr;
- request.exemptFromEntitlementCheck = exempt;
- request.showProvisioningUi = false;
- request.connectivityScope = scope;
-
- return request;
+ private TetheringRequest createTetheringRequest(final int type,
+ final LinkAddress localIPv4Address, final LinkAddress staticClientAddress,
+ final boolean exempt, final int scope) {
+ TetheringRequest.Builder builder = new TetheringRequest.Builder(type)
+ .setExemptFromEntitlementCheck(exempt)
+ .setConnectivityScope(scope)
+ .setShouldShowEntitlementUi(false);
+ if (localIPv4Address != null && staticClientAddress != null) {
+ builder.setStaticIpv4Addresses(localIPv4Address, staticClientAddress);
+ }
+ return builder.build();
}
@NonNull
@@ -911,7 +910,7 @@
private void prepareNcmTethering() {
// Emulate startTethering(TETHERING_NCM) called
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_NCM), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
@@ -919,7 +918,7 @@
private void prepareUsbTethering() {
// Emulate pressing the USB tethering button in Settings UI.
- final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
+ final TetheringRequest request = createTetheringRequest(TETHERING_USB);
mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
@@ -1083,8 +1082,8 @@
UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
- verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
assertSetIfaceToDadProxy(0 /* numOfCalls */, "" /* ifaceName */);
@@ -1112,8 +1111,8 @@
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
- verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
// TODO: add interfaceParams to compare in verify.
@@ -1128,8 +1127,8 @@
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
- verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -1146,13 +1145,12 @@
UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
runUsbTethering(upstreamState);
- verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).tetherAddForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_RNDIS_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME,
- TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_RNDIS_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
@@ -1173,10 +1171,10 @@
UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
- verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
upstreamState = buildMobile464xlatUpstreamState();
@@ -1188,12 +1186,11 @@
mLooper.dispatchAll();
// Forwarding is added for 464xlat
- verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME,
- TEST_XLAT_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_NCM_IFNAME, TEST_XLAT_MOBILE_IFNAME);
// Forwarding was not re-added for v6 (still times(1))
- verify(mNetd, times(1)).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager, times(1))
+ .addInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
// DHCP not restarted on downstream (still times(1))
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -1909,7 +1906,7 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
@@ -1938,7 +1935,7 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
@@ -1988,7 +1985,7 @@
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
@@ -2334,7 +2331,7 @@
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
tetherState = callback.pollTetherStatesChanged();
@@ -2430,11 +2427,11 @@
initTetheringOnTestThread();
final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_ETHERNET), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mEm, times(1)).requestTetheredInterface(any(), any());
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_ETHERNET), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verifyNoMoreInteractions(mEm);
@@ -2644,7 +2641,7 @@
final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
// Enable USB tethering and check that Tethering starts USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_USB), TEST_CALLER_PKG,
firstResult);
mLooper.dispatchAll();
firstResult.assertHasResult();
@@ -2652,7 +2649,7 @@
verifyNoMoreInteractions(mUsbManager);
// Enable USB tethering again with the same request and expect no change to USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_USB), TEST_CALLER_PKG,
secondResult);
mLooper.dispatchAll();
secondResult.assertHasResult();
@@ -2661,7 +2658,7 @@
// Enable USB tethering with a different request and expect that USB is stopped and
// started.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+ mTethering.startTethering(createTetheringRequest(TETHERING_USB,
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
TEST_CALLER_PKG, thirdResult);
mLooper.dispatchAll();
@@ -2692,7 +2689,7 @@
final int clientAddrParceled = 0xc0a8002a;
final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor =
ArgumentCaptor.forClass(DhcpServingParamsParcel.class);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+ mTethering.startTethering(createTetheringRequest(TETHERING_USB,
serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
TEST_CALLER_PKG, null);
mLooper.dispatchAll();
@@ -2820,8 +2817,8 @@
public void testExemptFromEntitlementCheck() throws Exception {
initTetheringOnTestThread();
setupForRequiredProvisioning();
- final TetheringRequestParcel wifiNotExemptRequest =
- createTetheringRequestParcel(TETHERING_WIFI, null, null, false,
+ final TetheringRequest wifiNotExemptRequest =
+ createTetheringRequest(TETHERING_WIFI, null, null, false,
CONNECTIVITY_SCOPE_GLOBAL);
mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
@@ -2834,8 +2831,8 @@
reset(mEntitleMgr);
setupForRequiredProvisioning();
- final TetheringRequestParcel wifiExemptRequest =
- createTetheringRequestParcel(TETHERING_WIFI, null, null, true,
+ final TetheringRequest wifiExemptRequest =
+ createTetheringRequest(TETHERING_WIFI, null, null, true,
CONNECTIVITY_SCOPE_GLOBAL);
mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
@@ -2954,7 +2951,7 @@
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET),
+ mTethering.startTethering(createTetheringRequest(TETHERING_ETHERNET),
TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
@@ -3235,7 +3232,7 @@
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
@@ -3272,7 +3269,7 @@
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
@@ -3294,7 +3291,7 @@
// already bound.
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, secondResult);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
@@ -3317,7 +3314,7 @@
initTetheringOnTestThread();
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
TEST_CALLER_PKG, result);
mLooper.dispatchAll();
ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
@@ -3436,8 +3433,7 @@
runUsbTethering(upstreamState);
verify(mNetd).interfaceGetList();
- verify(mNetd).tetherAddForward(expectedIface, TEST_MOBILE_IFNAME);
- verify(mNetd).ipfwdAddInterfaceForward(expectedIface, TEST_MOBILE_IFNAME);
+ verify(mRoutingCoordinatorManager).addInterfaceForward(expectedIface, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon).start();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
@@ -3487,7 +3483,7 @@
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
final ResultListener ncmResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_NCM), TEST_CALLER_PKG,
ncmResult);
mLooper.dispatchAll();
ncmResult.assertHasResult();
@@ -3638,7 +3634,7 @@
when(mWifiManager.startTetheredHotspot(any())).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ mTethering.startTethering(createTetheringRequest(TETHERING_WIFI), TEST_CALLER_PKG,
null);
mLooper.dispatchAll();
verify(mWifiManager).startTetheredHotspot(null);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index e2c924c..7cef9cb 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -46,10 +46,11 @@
import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.net.NetworkCapabilities;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
@@ -60,10 +61,12 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.networkstack.tethering.UpstreamNetworkState;
+import com.android.networkstack.tethering.metrics.TetheringMetrics.Dependencies;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@@ -76,28 +79,23 @@
private static final long TEST_START_TIME = 1670395936033L;
private static final long SECOND_IN_MILLIS = 1_000L;
+ @Mock private Context mContext;
+ @Mock private Dependencies mDeps;
+
private TetheringMetrics mTetheringMetrics;
private final NetworkTetheringReported.Builder mStatsBuilder =
NetworkTetheringReported.newBuilder();
private long mElapsedRealtime;
- private class MockTetheringMetrics extends TetheringMetrics {
- @Override
- public void write(final NetworkTetheringReported reported) {}
- @Override
- public long timeNow() {
- return currentTimeMillis();
- }
- }
-
private long currentTimeMillis() {
return TEST_START_TIME + mElapsedRealtime;
}
private void incrementCurrentTime(final long duration) {
mElapsedRealtime += duration;
- mTetheringMetrics.timeNow();
+ final long currentTimeMillis = currentTimeMillis();
+ doReturn(currentTimeMillis).when(mDeps).timeNow();
}
private long getElapsedRealtime() {
@@ -106,12 +104,14 @@
private void clearElapsedRealtime() {
mElapsedRealtime = 0;
+ doReturn(TEST_START_TIME).when(mDeps).timeNow();
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTetheringMetrics = spy(new MockTetheringMetrics());
+ doReturn(TEST_START_TIME).when(mDeps).timeNow();
+ mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mElapsedRealtime = 0L;
}
@@ -126,7 +126,7 @@
.setUpstreamEvents(upstreamEvents)
.setDurationMillis(duration)
.build();
- verify(mTetheringMetrics).write(expectedReport);
+ verify(mDeps).write(expectedReport);
}
private void updateErrorAndSendReport(final int downstream, final int error) {
@@ -162,6 +162,7 @@
private void runDownstreamTypesTest(final int type, final DownstreamType expectedResult)
throws Exception {
+ mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
final long duration = 2 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
@@ -172,9 +173,7 @@
verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN,
upstreamEvents, getElapsedRealtime());
- reset(mTetheringMetrics);
clearElapsedRealtime();
- mTetheringMetrics.cleanup();
}
@Test
@@ -189,6 +188,7 @@
private void runErrorCodesTest(final int errorCode, final ErrorCode expectedResult)
throws Exception {
+ mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
final long duration = 2 * SECOND_IN_MILLIS;
@@ -199,9 +199,7 @@
addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN,
upstreamEvents, getElapsedRealtime());
- reset(mTetheringMetrics);
clearElapsedRealtime();
- mTetheringMetrics.cleanup();
}
@Test
@@ -231,6 +229,7 @@
private void runUserTypesTest(final String callerPkg, final UserType expectedResult)
throws Exception {
+ mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
final long duration = 1 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
@@ -241,9 +240,7 @@
addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult,
upstreamEvents, getElapsedRealtime());
- reset(mTetheringMetrics);
clearElapsedRealtime();
- mTetheringMetrics.cleanup();
}
@Test
@@ -256,6 +253,7 @@
private void runUpstreamTypesTest(final UpstreamNetworkState ns,
final UpstreamType expectedResult) throws Exception {
+ mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
mTetheringMetrics.maybeUpdateUpstreamType(ns);
final long duration = 2 * SECOND_IN_MILLIS;
@@ -266,9 +264,7 @@
addUpstreamEvent(upstreamEvents, expectedResult, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_UNKNOWN, upstreamEvents, getElapsedRealtime());
- reset(mTetheringMetrics);
clearElapsedRealtime();
- mTetheringMetrics.cleanup();
}
@Test
@@ -379,4 +375,21 @@
UserType.USER_SETTINGS, upstreamEvents,
currentTimeMillis() - wifiTetheringStartTime);
}
+
+ private void runUsageSupportedForUpstreamTypeTest(final UpstreamType upstreamType,
+ final boolean isSupported) {
+ final boolean result = TetheringMetrics.isUsageSupportedForUpstreamType(upstreamType);
+ assertEquals(isSupported, result);
+ }
+
+ @Test
+ public void testUsageSupportedForUpstreamTypeTest() {
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_CELLULAR, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_WIFI, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_BLUETOOTH, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_ETHERNET, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_WIFI_AWARE, false /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_LOWPAN, false /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_UNKNOWN, false /* isSupported */);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
index 94ce2b6..f0770f9 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
@@ -15,8 +15,6 @@
*/
package com.android.networkstack.tethering.util;
-import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
-import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.EAGAIN;
@@ -25,8 +23,6 @@
import static android.system.OsConstants.SOCK_NONBLOCK;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import android.net.LinkAddress;
import android.net.MacAddress;
@@ -43,9 +39,7 @@
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
import com.android.net.module.util.structs.Ipv6Header;
-import com.android.testutils.MiscAsserts;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,13 +55,6 @@
private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
private static final int PACKET_SIZE = 1500;
- private TetheringRequestParcel mTetheringRequest;
-
- @Before
- public void setUp() {
- mTetheringRequest = makeTetheringRequestParcel();
- }
-
public TetheringRequestParcel makeTetheringRequestParcel() {
final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = TETHERING_WIFI;
@@ -78,40 +65,6 @@
return request;
}
- @Test
- public void testIsTetheringRequestEquals() {
- TetheringRequestParcel request = makeTetheringRequestParcel();
-
- assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest));
- assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
- assertTrue(TetheringUtils.isTetheringRequestEquals(null, null));
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null));
- assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest));
-
- request = makeTetheringRequestParcel();
- request.tetheringType = TETHERING_USB;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.localIPv4Address = null;
- request.staticClientAddress = null;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.exemptFromEntitlementCheck = true;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.showProvisioningUi = false;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.connectivityScope = CONNECTIVITY_SCOPE_LOCAL;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- MiscAsserts.assertFieldCountEquals(6, TetheringRequestParcel.class);
- }
-
// Writes the specified packet to a filedescriptor, skipping the Ethernet header.
// Needed because the Ipv6Utils methods for building packets always include the Ethernet header,
// but socket filters applied by TetheringUtils expect the packet to start from the IP header.
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 1958aa8..0c21c9b 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -87,6 +87,7 @@
bpf {
name: "offload.o",
srcs: ["offload.c"],
+ btf: false,
cflags: [
"-Wall",
"-Werror",
@@ -107,6 +108,7 @@
bpf {
name: "test.o",
srcs: ["test.c"],
+ btf: false,
cflags: [
"-Wall",
"-Werror",
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index 1511ee5..ba2f26b 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -83,7 +83,7 @@
// try to make the first 'len' header bytes readable/writable via direct packet access
// (note: AFAIK there is no way to ask for only direct packet read without also getting write)
-static inline __always_inline void try_make_writable(struct __sk_buff* skb, int len) {
+static inline __always_inline void try_make_writable(struct __sk_buff* skb, unsigned len) {
if (len > skb->len) len = skb->len;
if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len);
}
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index f83e5ae..da6ccbf 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -177,7 +177,7 @@
// Calculate the IPv4 one's complement checksum of the IPv4 header.
__wsum sum4 = 0;
- for (int i = 0; i < sizeof(ip) / sizeof(__u16); ++i) {
+ for (unsigned i = 0; i < sizeof(ip) / sizeof(__u16); ++i) {
sum4 += ((__u16*)&ip)[i];
}
// Note that sum4 is guaranteed to be non-zero by virtue of ip.version == 4
@@ -188,7 +188,7 @@
// Calculate the *negative* IPv6 16-bit one's complement checksum of the IPv6 header.
__wsum sum6 = 0;
// We'll end up with a non-zero sum due to ip6->version == 6 (which has '0' bits)
- for (int i = 0; i < sizeof(*ip6) / sizeof(__u16); ++i) {
+ for (unsigned i = 0; i < sizeof(*ip6) / sizeof(__u16); ++i) {
sum6 += ~((__u16*)ip6)[i]; // note the bitwise negation
}
@@ -321,7 +321,7 @@
// Calculate the IPv4 one's complement checksum of the IPv4 header.
__wsum sum4 = 0;
- for (int i = 0; i < sizeof(*ip4) / sizeof(__u16); ++i) {
+ for (unsigned i = 0; i < sizeof(*ip4) / sizeof(__u16); ++i) {
sum4 += ((__u16*)ip4)[i];
}
// Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
@@ -387,7 +387,7 @@
// Calculate the IPv6 16-bit one's complement checksum of the IPv6 header.
__wsum sum6 = 0;
// We'll end up with a non-zero sum due to ip6.version == 6
- for (int i = 0; i < sizeof(ip6) / sizeof(__u16); ++i) {
+ for (unsigned i = 0; i < sizeof(ip6) / sizeof(__u16); ++i) {
sum6 += ((__u16*)&ip6)[i];
}
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index b3cde45..f29bbf9 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -184,7 +184,7 @@
static __always_inline inline void update_##the_stats_map(const struct __sk_buff* const skb, \
const TypeOfKey* const key, \
const struct egress_bool egress, \
- const struct kver_uint kver) { \
+ __unused const struct kver_uint kver) { \
StatsValue* value = bpf_##the_stats_map##_lookup_elem(key); \
if (!value) { \
StatsValue newValue = {}; \
@@ -676,7 +676,7 @@
DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet_create", AID_ROOT, AID_ROOT, inet_socket_create,
KVER_4_14)
-(struct bpf_sock* sk) {
+(__unused struct bpf_sock* sk) {
// A return value of 1 means allow, everything else means deny.
return (get_app_permissions() & BPF_PERMISSION_INTERNET) ? 1 : 0;
}
@@ -690,7 +690,7 @@
return 1;
}
-static __always_inline inline int check_localhost(struct bpf_sock_addr *ctx) {
+static __always_inline inline int check_localhost(__unused struct bpf_sock_addr *ctx) {
// See include/uapi/linux/bpf.h:
//
// struct bpf_sock_addr {
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 4f152bf..4d908d2 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -127,7 +127,7 @@
static inline __always_inline int do_forward6(struct __sk_buff* skb,
const struct rawip_bool rawip,
const struct stream_bool stream,
- const struct kver_uint kver) {
+ __unused const struct kver_uint kver) {
const bool is_ethernet = !rawip.rawip;
// Must be meta-ethernet IPv6 frame
@@ -343,13 +343,13 @@
// and define no-op stubs for pre-4.14 kernels.
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER_4_14)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER_4_14)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
@@ -363,7 +363,7 @@
const int l2_header_size, void* data, const void* data_end,
struct ethhdr* eth, struct iphdr* ip, const struct rawip_bool rawip,
const struct stream_bool stream, const struct updatetime_bool updatetime,
- const bool is_tcp, const struct kver_uint kver) {
+ const bool is_tcp, __unused const struct kver_uint kver) {
const bool is_ethernet = !rawip.rawip;
struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL;
struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);
@@ -593,7 +593,7 @@
// Calculate the IPv4 one's complement checksum of the IPv4 header.
__wsum sum4 = 0;
- for (int i = 0; i < sizeof(*ip) / sizeof(__u16); ++i) {
+ for (unsigned i = 0; i < sizeof(*ip) / sizeof(__u16); ++i) {
sum4 += ((__u16*)ip)[i];
}
// Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
@@ -780,13 +780,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_rawip_stub, KVER_NONE, KVER_5_4)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_rawip_stub, KVER_NONE, KVER_5_4)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
@@ -794,13 +794,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_ether_stub, KVER_NONE, KVER_4_14)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$stub", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_ether_stub, KVER_NONE, KVER_4_14)
-(struct __sk_buff* skb) {
+(__unused struct __sk_buff* skb) {
return TC_ACT_PIPE;
}
@@ -808,13 +808,13 @@
DEFINE_BPF_MAP_GRW(tether_dev_map, DEVMAP_HASH, uint32_t, uint32_t, 64, TETHERING_GID)
-static inline __always_inline int do_xdp_forward6(struct xdp_md *ctx, const struct rawip_bool rawip,
- const struct stream_bool stream) {
+static inline __always_inline int do_xdp_forward6(__unused struct xdp_md *ctx,
+ __unused const struct rawip_bool rawip, __unused const struct stream_bool stream) {
return XDP_PASS;
}
-static inline __always_inline int do_xdp_forward4(struct xdp_md *ctx, const struct rawip_bool rawip,
- const struct stream_bool stream) {
+static inline __always_inline int do_xdp_forward4(__unused struct xdp_md *ctx,
+ __unused const struct rawip_bool rawip, __unused const struct stream_bool stream) {
return XDP_PASS;
}
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 1f1953c..52de1a3 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -497,16 +497,26 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.thread.PendingOperationalDataset> CREATOR;
}
+ @FlaggedApi("com.android.net.thread.flags.configuration_enabled") public final class ThreadConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean isDhcpv6PdEnabled();
+ method public boolean isNat64Enabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.thread.ThreadConfiguration> CREATOR;
+ }
+
@FlaggedApi("com.android.net.thread.flags.thread_enabled") public final class ThreadNetworkController {
method public void createRandomizedDataset(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.thread.ActiveOperationalDataset,android.net.thread.ThreadNetworkException>);
method public int getThreadVersion();
method public static boolean isAttached(int);
method @RequiresPermission("android.permission.THREAD_NETWORK_PRIVILEGED") public void join(@NonNull android.net.thread.ActiveOperationalDataset, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.net.thread.ThreadNetworkException>);
method @RequiresPermission("android.permission.THREAD_NETWORK_PRIVILEGED") public void leave(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.net.thread.ThreadNetworkException>);
+ method @FlaggedApi("com.android.net.thread.flags.configuration_enabled") @RequiresPermission(android.Manifest.permission.THREAD_NETWORK_PRIVILEGED) public void registerConfigurationCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.net.thread.ThreadConfiguration>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_NETWORK_STATE, "android.permission.THREAD_NETWORK_PRIVILEGED"}) public void registerOperationalDatasetCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.thread.ThreadNetworkController.OperationalDatasetCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.thread.ThreadNetworkController.StateCallback);
method @RequiresPermission("android.permission.THREAD_NETWORK_PRIVILEGED") public void scheduleMigration(@NonNull android.net.thread.PendingOperationalDataset, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.net.thread.ThreadNetworkException>);
method @RequiresPermission("android.permission.THREAD_NETWORK_PRIVILEGED") public void setEnabled(boolean, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.net.thread.ThreadNetworkException>);
+ method @FlaggedApi("com.android.net.thread.flags.configuration_enabled") @RequiresPermission(android.Manifest.permission.THREAD_NETWORK_PRIVILEGED) public void unregisterConfigurationCallback(@NonNull java.util.function.Consumer<android.net.thread.ThreadConfiguration>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_NETWORK_STATE, "android.permission.THREAD_NETWORK_PRIVILEGED"}) public void unregisterOperationalDatasetCallback(@NonNull android.net.thread.ThreadNetworkController.OperationalDatasetCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void unregisterStateCallback(@NonNull android.net.thread.ThreadNetworkController.StateCallback);
field public static final int DEVICE_ROLE_CHILD = 2; // 0x2
diff --git a/framework/Android.bp b/framework/Android.bp
index deb1c5a..282ba4b 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -95,8 +95,7 @@
"framework-connectivity-javastream-protos",
],
impl_only_static_libs: [
- "net-utils-device-common-bpf",
- "net-utils-device-common-struct-base",
+ "net-utils-framework-connectivity",
],
libs: [
"androidx.annotation_annotation",
@@ -109,22 +108,20 @@
],
}
+// Library to allow Cronet to use hidden APIs
java_library {
- name: "framework-connectivity-pre-jarjar",
+ name: "framework-connectivity-pre-jarjar-without-cronet",
defaults: [
"framework-connectivity-defaults",
],
static_libs: [
- "httpclient_api",
- "httpclient_impl",
// Framework-connectivity-pre-jarjar is identical to framework-connectivity
// implementation, but without the jarjar rules. However, framework-connectivity
// is not based on framework-connectivity-pre-jarjar, it's rebuilt from source
// to generate the SDK stubs.
// Even if the library is included in "impl_only_static_libs" of defaults. This is still
// needed because java_library which doesn't understand "impl_only_static_libs".
- "net-utils-device-common-bpf",
- "net-utils-device-common-struct-base",
+ "net-utils-framework-connectivity",
],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
@@ -135,6 +132,21 @@
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
+ visibility: ["//external/cronet:__subpackages__"],
+}
+
+java_library {
+ name: "framework-connectivity-pre-jarjar",
+ defaults: ["framework-module-defaults"],
+ min_sdk_version: "30",
+ static_libs: [
+ "framework-connectivity-pre-jarjar-without-cronet",
+ "httpclient_api",
+ "httpclient_impl",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
@@ -332,7 +344,6 @@
srcs: [
// Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.S)
// or above as appropriate so that API checks are enforced for R+ users of this library
- "src/android/net/RoutingCoordinatorManager.java",
"src/android/net/connectivity/ConnectivityInternalApiUtil.java",
],
visibility: [
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index ffaf41f..a6a967b 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4489,6 +4489,7 @@
public static final int CALLBACK_BLK_CHANGED = 11;
/** @hide */
public static final int CALLBACK_LOCAL_NETWORK_INFO_CHANGED = 12;
+ // When adding new IDs, note CallbackQueue assumes callback IDs are at most 16 bits.
/** @hide */
@@ -6737,21 +6738,11 @@
}
}
- private static final Object sRoutingCoordinatorManagerLock = new Object();
- @GuardedBy("sRoutingCoordinatorManagerLock")
- private static RoutingCoordinatorManager sRoutingCoordinatorManager = null;
/** @hide */
@RequiresApi(Build.VERSION_CODES.S)
- public RoutingCoordinatorManager getRoutingCoordinatorManager() {
+ public IBinder getRoutingCoordinatorService() {
try {
- synchronized (sRoutingCoordinatorManagerLock) {
- if (null == sRoutingCoordinatorManager) {
- sRoutingCoordinatorManager = new RoutingCoordinatorManager(mContext,
- IRoutingCoordinator.Stub.asInterface(
- mService.getRoutingCoordinatorService()));
- }
- return sRoutingCoordinatorManager;
- }
+ return mService.getRoutingCoordinatorService();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java b/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
index 79f1f65..6e87ed3 100644
--- a/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
+++ b/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.RoutingCoordinatorManager;
import android.os.Build;
import android.os.IBinder;
@@ -54,8 +53,8 @@
* @return an instance of the coordinator manager
*/
@RequiresApi(Build.VERSION_CODES.S)
- public static RoutingCoordinatorManager getRoutingCoordinatorManager(Context ctx) {
+ public static IBinder getRoutingCoordinator(Context ctx) {
final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
- return cm.getRoutingCoordinatorManager();
+ return cm.getRoutingCoordinatorService();
}
}
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 4779b47..9682545 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -260,7 +260,10 @@
}
Status BpfHandler::initMaps() {
- mapLockTest();
+ // bpfLock() requires bpfGetFdMapId which is only available on 4.14+ kernels.
+ if (bpf::isAtLeastKernelVersion(4, 14, 0)) {
+ mapLockTest();
+ }
RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 64624ae..419ec3a 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -93,6 +93,7 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.InetAddressUtils;
import com.android.net.module.util.PermissionUtils;
@@ -768,7 +769,7 @@
private Set<String> dedupSubtypeLabels(Collection<String> subtypes) {
final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size());
for (String subtype : subtypes) {
- subtypeMap.put(MdnsUtils.toDnsLowerCase(subtype), subtype);
+ subtypeMap.put(DnsUtils.toDnsUpperCase(subtype), subtype);
}
return new ArraySet<>(subtypeMap.values());
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 54943c7..f55db93 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -24,6 +24,7 @@
import android.util.Pair;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
@@ -193,7 +194,7 @@
// Ignore any PTR records that don't match the current query.
if (!CollectionUtils.any(questions,
q -> q instanceof MdnsPointerRecord
- && MdnsUtils.equalsDnsLabelIgnoreDnsCase(
+ && DnsUtils.equalsDnsLabelIgnoreDnsCase(
q.getName(), ptrRecord.getName()))) {
continue;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index b870477..9c52eca 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -41,6 +41,7 @@
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.ConnectivityResources;
import com.android.server.connectivity.mdns.util.MdnsUtils;
@@ -272,7 +273,7 @@
return true;
}
// Check if it conflicts with the default hostname.
- return MdnsUtils.equalsIgnoreDnsCase(newInfo.getHostname(), mDeviceHostName[0]);
+ return DnsUtils.equalsIgnoreDnsCase(newInfo.getHostname(), mDeviceHostName[0]);
}
private void updateRegistrationUntilNoConflict(
@@ -436,8 +437,8 @@
continue;
}
final NsdServiceInfo other = mPendingRegistrations.valueAt(i).getServiceInfo();
- if (MdnsUtils.equalsIgnoreDnsCase(info.getServiceName(), other.getServiceName())
- && MdnsUtils.equalsIgnoreDnsCase(info.getServiceType(),
+ if (DnsUtils.equalsIgnoreDnsCase(info.getServiceName(), other.getServiceName())
+ && DnsUtils.equalsIgnoreDnsCase(info.getServiceType(),
other.getServiceType())) {
return mPendingRegistrations.keyAt(i);
}
@@ -463,13 +464,13 @@
final NsdServiceInfo otherInfo = otherRegistration.getServiceInfo();
final int otherServiceId = mPendingRegistrations.keyAt(i);
if (clientUid != otherRegistration.mClientUid
- && MdnsUtils.equalsIgnoreDnsCase(
+ && DnsUtils.equalsIgnoreDnsCase(
info.getHostname(), otherInfo.getHostname())) {
return otherServiceId;
}
if (!info.getHostAddresses().isEmpty()
&& !otherInfo.getHostAddresses().isEmpty()
- && MdnsUtils.equalsIgnoreDnsCase(
+ && DnsUtils.equalsIgnoreDnsCase(
info.getHostname(), otherInfo.getHostname())) {
return otherServiceId;
}
@@ -849,7 +850,7 @@
sharedLog.wtf("Invalid priority in config_nsdOffloadServicesPriority: " + entry);
continue;
}
- priorities.put(MdnsUtils.toDnsLowerCase(priorityAndType[1]), priority);
+ priorities.put(DnsUtils.toDnsUpperCase(priorityAndType[1]), priority);
}
return priorities;
}
@@ -995,7 +996,7 @@
@NonNull Registration registration, byte[] rawOffloadPacket) {
final NsdServiceInfo nsdServiceInfo = registration.getServiceInfo();
final Integer mapPriority = mServiceTypeToOffloadPriority.get(
- MdnsUtils.toDnsLowerCase(nsdServiceInfo.getServiceType()));
+ DnsUtils.toDnsUpperCase(nsdServiceInfo.getServiceType()));
// Higher values of priority are less prioritized
final int priority = mapPriority == null ? Integer.MAX_VALUE : mapPriority;
final OffloadServiceInfo offloadServiceInfo = new OffloadServiceInfo(
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 0ab7a76..8123d27 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -30,6 +30,7 @@
import androidx.annotation.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
@@ -66,8 +67,8 @@
public void put(@NonNull String serviceType, @NonNull SocketKey socketKey,
@NonNull MdnsServiceTypeClient client) {
- final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
- final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsLowerServiceType,
+ final String dnsUpperServiceType = DnsUtils.toDnsUpperCase(serviceType);
+ final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsUpperServiceType,
socketKey);
clients.put(perSocketServiceType, client);
}
@@ -75,18 +76,18 @@
@Nullable
public MdnsServiceTypeClient get(
@NonNull String serviceType, @NonNull SocketKey socketKey) {
- final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
- final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsLowerServiceType,
+ final String dnsUpperServiceType = DnsUtils.toDnsUpperCase(serviceType);
+ final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsUpperServiceType,
socketKey);
return clients.getOrDefault(perSocketServiceType, null);
}
public List<MdnsServiceTypeClient> getByServiceType(@NonNull String serviceType) {
- final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
+ final String dnsUpperServiceType = DnsUtils.toDnsUpperCase(serviceType);
final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) {
final Pair<String, SocketKey> perSocketServiceType = clients.keyAt(i);
- if (dnsLowerServiceType.equals(perSocketServiceType.first)) {
+ if (dnsUpperServiceType.equals(perSocketServiceType.first)) {
list.add(clients.valueAt(i));
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index fcfb15f..c575d40 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -29,7 +29,9 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -225,6 +227,12 @@
Log.wtf(TAG, "No mDns packets to send");
return;
}
+ // Check all packets with the same address
+ if (!MdnsUtils.checkAllPacketsWithSameAddress(packets)) {
+ Log.wtf(TAG, "Some mDNS packets have a different target address. addresses="
+ + CollectionUtils.map(packets, DatagramPacket::getSocketAddress));
+ return;
+ }
final boolean isIpv6 = ((InetSocketAddress) packets.get(0).getSocketAddress())
.getAddress() instanceof Inet6Address;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
index 1239180..a5b8803 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
@@ -20,7 +20,7 @@
import android.text.TextUtils;
import com.android.net.module.util.CollectionUtils;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
+import com.android.net.module.util.DnsUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -153,7 +153,7 @@
@Override
public int hashCode() {
return Objects.hash(super.hashCode(),
- Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(mNextDomain)),
+ Arrays.hashCode(DnsUtils.toDnsLabelsUpperCase(mNextDomain)),
Arrays.hashCode(mTypes));
}
@@ -167,7 +167,7 @@
}
return super.equals(other)
- && MdnsUtils.equalsDnsLabelIgnoreDnsCase(mNextDomain,
+ && DnsUtils.equalsDnsLabelIgnoreDnsCase(mNextDomain,
((MdnsNsecRecord) other).mNextDomain)
&& Arrays.equals(mTypes, ((MdnsNsecRecord) other).mTypes);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
index 6879a64..cf788be 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
@@ -16,8 +16,8 @@
package com.android.server.connectivity.mdns;
+import com.android.net.module.util.DnsUtils;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -180,7 +180,7 @@
int existingOffset = entry.getKey();
String[] existingLabels = entry.getValue();
- if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(existingLabels, labels)) {
+ if (DnsUtils.equalsDnsLabelIgnoreDnsCase(existingLabels, labels)) {
writePointer(existingOffset);
return;
} else if (MdnsRecord.labelsAreSuffix(existingLabels, labels)) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
index e5c90a4..39bf653 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
@@ -20,7 +20,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
+import com.android.net.module.util.DnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -68,7 +68,7 @@
}
public boolean hasSubtype() {
- return (name != null) && (name.length > 2) && MdnsUtils.equalsIgnoreDnsCase(name[1],
+ return (name != null) && (name.length > 2) && DnsUtils.equalsIgnoreDnsCase(name[1],
MdnsConstants.SUBTYPE_LABEL);
}
@@ -83,7 +83,7 @@
@Override
public int hashCode() {
- return (super.hashCode() * 31) + Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(pointer));
+ return (super.hashCode() * 31) + Arrays.hashCode(DnsUtils.toDnsLabelsUpperCase(pointer));
}
@Override
@@ -95,7 +95,7 @@
return false;
}
- return super.equals(other) && MdnsUtils.equalsDnsLabelIgnoreDnsCase(pointer,
+ return super.equals(other) && DnsUtils.equalsDnsLabelIgnoreDnsCase(pointer,
((MdnsPointerRecord) other).pointer);
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
index e88947a..4a44fff 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
@@ -23,8 +23,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -114,7 +114,7 @@
private static boolean containsName(@NonNull List<MdnsRecord> records,
@NonNull String[] name) {
return CollectionUtils.any(records,
- r -> MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, r.getName()));
+ r -> DnsUtils.equalsDnsLabelIgnoreDnsCase(name, r.getName()));
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
index 3fcf0d4..5c02767 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
@@ -63,17 +63,22 @@
* rescheduling is not necessary.
*/
@Nullable
- public ScheduledQueryTaskArgs maybeRescheduleCurrentRun(long now,
- long minRemainingTtl, long lastSentTime, long sessionId) {
+ public ScheduledQueryTaskArgs maybeRescheduleCurrentRun(
+ long now,
+ long minRemainingTtl,
+ long lastSentTime,
+ long sessionId,
+ int numOfQueriesBeforeBackoff) {
if (mLastScheduledQueryTaskArgs == null) {
return null;
}
- if (!mLastScheduledQueryTaskArgs.config.shouldUseQueryBackoff()) {
+ if (!mLastScheduledQueryTaskArgs.config.shouldUseQueryBackoff(numOfQueriesBeforeBackoff)) {
return null;
}
final long timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
- mLastScheduledQueryTaskArgs.config, now, minRemainingTtl, lastSentTime);
+ mLastScheduledQueryTaskArgs.config, now, minRemainingTtl, lastSentTime,
+ numOfQueriesBeforeBackoff);
if (timeToRun <= mLastScheduledQueryTaskArgs.timeToRun) {
return null;
@@ -95,14 +100,16 @@
long minRemainingTtl,
long now,
long lastSentTime,
- long sessionId) {
- final QueryTaskConfig nextRunConfig = currentConfig.getConfigForNextRun();
+ long sessionId,
+ int queryMode,
+ int numOfQueriesBeforeBackoff) {
+ final QueryTaskConfig nextRunConfig = currentConfig.getConfigForNextRun(queryMode);
final long timeToRun;
if (mLastScheduledQueryTaskArgs == null) {
timeToRun = now + nextRunConfig.delayUntilNextTaskWithoutBackoffMs;
} else {
timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
- nextRunConfig, now, minRemainingTtl, lastSentTime);
+ nextRunConfig, now, minRemainingTtl, lastSentTime, numOfQueriesBeforeBackoff);
}
mLastScheduledQueryTaskArgs = new ScheduledQueryTaskArgs(nextRunConfig, timeToRun,
minRemainingTtl + now,
@@ -122,9 +129,10 @@
}
private static long calculateTimeToRun(@NonNull ScheduledQueryTaskArgs taskArgs,
- QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime) {
+ QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime,
+ int numOfQueriesBeforeBackoff) {
final long baseDelayInMs = queryTaskConfig.delayUntilNextTaskWithoutBackoffMs;
- if (!queryTaskConfig.shouldUseQueryBackoff()) {
+ if (!queryTaskConfig.shouldUseQueryBackoff(numOfQueriesBeforeBackoff)) {
return lastSentTime + baseDelayInMs;
}
if (minRemainingTtl <= 0) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
index b865319..d464ca7 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -25,7 +25,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
+import com.android.net.module.util.DnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -139,7 +139,7 @@
}
for (int i = 0; i < list1.length; ++i) {
- if (!MdnsUtils.equalsIgnoreDnsCase(list1[i], list2[i + offset])) {
+ if (!DnsUtils.equalsIgnoreDnsCase(list1[i], list2[i + offset])) {
return false;
}
}
@@ -284,13 +284,13 @@
MdnsRecord otherRecord = (MdnsRecord) other;
- return MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, otherRecord.name) && (type
+ return DnsUtils.equalsDnsLabelIgnoreDnsCase(name, otherRecord.name) && (type
== otherRecord.type);
}
@Override
public int hashCode() {
- return Objects.hash(Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(name)), type);
+ return Objects.hash(Arrays.hashCode(DnsUtils.toDnsLabelsUpperCase(name)), type);
}
/**
@@ -311,7 +311,7 @@
public Key(int recordType, String[] recordName) {
this.recordType = recordType;
- this.recordName = MdnsUtils.toDnsLabelsLowerCase(recordName);
+ this.recordName = DnsUtils.toDnsLabelsUpperCase(recordName);
}
@Override
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index 36f3982..0e84764 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.HexDump;
import com.android.server.connectivity.mdns.util.MdnsUtils;
@@ -494,8 +495,8 @@
}
for (int i = 0; i < mServices.size(); i++) {
final NsdServiceInfo info = mServices.valueAt(i).serviceInfo;
- if (MdnsUtils.equalsIgnoreDnsCase(serviceName, info.getServiceName())
- && MdnsUtils.equalsIgnoreDnsCase(serviceType, info.getServiceType())) {
+ if (DnsUtils.equalsIgnoreDnsCase(serviceName, info.getServiceName())
+ && DnsUtils.equalsIgnoreDnsCase(serviceType, info.getServiceType())) {
return mServices.keyAt(i);
}
}
@@ -821,7 +822,7 @@
must match the question qtype unless the qtype is "ANY" (255) or the rrtype is
"CNAME" (5), and the record rrclass must match the question qclass unless the
qclass is "ANY" (255) */
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(info.record.getName(), question.getName())) {
+ if (!DnsUtils.equalsDnsLabelIgnoreDnsCase(info.record.getName(), question.getName())) {
continue;
}
hasFullyOwnedNameMatch |= !info.isSharedName;
@@ -1232,7 +1233,7 @@
return RecordConflictType.NO_CONFLICT;
}
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(), fullServiceName)) {
+ if (!DnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(), fullServiceName)) {
return RecordConflictType.NO_CONFLICT;
}
@@ -1270,7 +1271,7 @@
}
// Different names. There won't be a conflict.
- if (!MdnsUtils.equalsIgnoreDnsCase(
+ if (!DnsUtils.equalsIgnoreDnsCase(
record.getName()[0], registration.serviceInfo.getHostname())) {
return RecordConflictType.NO_CONFLICT;
}
@@ -1351,7 +1352,7 @@
int id = mServices.keyAt(i);
ServiceRegistration service = mServices.valueAt(i);
if (service.exiting) continue;
- if (MdnsUtils.equalsIgnoreDnsCase(service.serviceInfo.getHostname(), hostname)) {
+ if (DnsUtils.equalsIgnoreDnsCase(service.serviceInfo.getHostname(), hostname)) {
consumer.accept(id, service);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
index 05ad1be..3636644 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -21,7 +21,7 @@
import android.net.Network;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
+import com.android.net.module.util.DnsUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -111,7 +111,7 @@
* pointer record is already present in the response with the same TTL.
*/
public synchronized boolean addPointerRecord(MdnsPointerRecord pointerRecord) {
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceName, pointerRecord.getPointer())) {
+ if (!DnsUtils.equalsDnsLabelIgnoreDnsCase(serviceName, pointerRecord.getPointer())) {
throw new IllegalArgumentException(
"Pointer records for different service names cannot be added");
}
@@ -305,13 +305,13 @@
boolean dropAddressRecords = false;
for (MdnsInetAddressRecord inetAddressRecord : getInet4AddressRecords()) {
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
+ if (!DnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
}
for (MdnsInetAddressRecord inetAddressRecord : getInet6AddressRecords()) {
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
+ if (!DnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index b812bb4..52e76ad 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -22,6 +22,7 @@
import android.util.ArrayMap;
import android.util.Pair;
+import com.android.net.module.util.DnsUtils;
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.EOFException;
@@ -49,7 +50,7 @@
List<MdnsResponse> responses, String[] pointer) {
if (responses != null) {
for (MdnsResponse response : responses) {
- if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(response.getServiceName(), pointer)) {
+ if (DnsUtils.equalsDnsLabelIgnoreDnsCase(response.getServiceName(), pointer)) {
return response;
}
}
@@ -65,7 +66,7 @@
if (serviceRecord == null) {
continue;
}
- if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(),
+ if (DnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(),
hostName)) {
return response;
}
@@ -318,7 +319,7 @@
if (serviceRecord == null) {
continue;
}
- if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(), hostName)) {
+ if (DnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(), hostName)) {
if (result == null) {
result = new ArrayList<>(/* initialCapacity= */ responses.size());
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
index e9a41d1..7eea93a 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
@@ -16,10 +16,10 @@
package com.android.server.connectivity.mdns;
+import static com.android.net.module.util.DnsUtils.equalsIgnoreDnsCase;
+import static com.android.net.module.util.DnsUtils.toDnsUpperCase;
import static com.android.server.connectivity.mdns.MdnsResponse.EXPIRATION_NEVER;
import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
-import static com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase;
-import static com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLowerCase;
import static java.lang.Math.min;
@@ -49,16 +49,16 @@
*/
public class MdnsServiceCache {
static class CacheKey {
- @NonNull final String mLowercaseServiceType;
+ @NonNull final String mUpperCaseServiceType;
@NonNull final SocketKey mSocketKey;
CacheKey(@NonNull String serviceType, @NonNull SocketKey socketKey) {
- mLowercaseServiceType = toDnsLowerCase(serviceType);
+ mUpperCaseServiceType = toDnsUpperCase(serviceType);
mSocketKey = socketKey;
}
@Override public int hashCode() {
- return Objects.hash(mLowercaseServiceType, mSocketKey);
+ return Objects.hash(mUpperCaseServiceType, mSocketKey);
}
@Override public boolean equals(Object other) {
@@ -68,7 +68,7 @@
if (!(other instanceof CacheKey)) {
return false;
}
- return Objects.equals(mLowercaseServiceType, ((CacheKey) other).mLowercaseServiceType)
+ return Objects.equals(mUpperCaseServiceType, ((CacheKey) other).mUpperCaseServiceType)
&& Objects.equals(mSocketKey, ((CacheKey) other).mSocketKey);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
index 0d6a9ec..fd716d2 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
@@ -20,7 +20,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsUtils;
+import com.android.net.module.util.DnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -151,7 +151,7 @@
public int hashCode() {
return (super.hashCode() * 31)
+ Objects.hash(servicePriority, serviceWeight,
- Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(serviceHost)),
+ Arrays.hashCode(DnsUtils.toDnsLabelsUpperCase(serviceHost)),
servicePort);
}
@@ -168,7 +168,7 @@
return super.equals(other)
&& (servicePriority == otherRecord.servicePriority)
&& (serviceWeight == otherRecord.serviceWeight)
- && MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceHost, otherRecord.serviceHost)
+ && DnsUtils.equalsDnsLabelIgnoreDnsCase(serviceHost, otherRecord.serviceHost)
&& (servicePort == otherRecord.servicePort);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index b3bdbe0..8959c1b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -33,6 +33,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DnsUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsUtils;
@@ -120,11 +121,11 @@
* @return true if the service name was not discovered before.
*/
boolean setServiceDiscovered(@NonNull String serviceName) {
- return discoveredServiceNames.add(MdnsUtils.toDnsLowerCase(serviceName));
+ return discoveredServiceNames.add(DnsUtils.toDnsUpperCase(serviceName));
}
void unsetServiceDiscovered(@NonNull String serviceName) {
- discoveredServiceNames.remove(MdnsUtils.toDnsLowerCase(serviceName));
+ discoveredServiceNames.remove(DnsUtils.toDnsUpperCase(serviceName));
}
}
@@ -147,7 +148,8 @@
final List<MdnsResponse> servicesToResolve = makeResponsesForResolve(socketKey);
final QueryTask queryTask = new QueryTask(taskArgs, servicesToResolve,
getAllDiscoverySubtypes(), needSendDiscoveryQueries(listeners),
- getExistingServices());
+ getExistingServices(), searchOptions.onlyUseIpv6OnIpv6OnlyNetworks(),
+ socketKey);
executor.submit(queryTask);
break;
}
@@ -178,7 +180,9 @@
minRemainingTtl,
now,
lastSentTime,
- sentResult.taskArgs.sessionId
+ sentResult.taskArgs.sessionId,
+ searchOptions.getQueryMode(),
+ searchOptions.numOfQueriesBeforeBackoff()
);
dependencies.sendMessageDelayed(
handler,
@@ -303,8 +307,8 @@
serviceCache.unregisterServiceExpiredCallback(cacheKey);
}
- private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
- @NonNull MdnsResponse response, @NonNull String[] serviceTypeLabels) {
+ private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(@NonNull MdnsResponse response,
+ @NonNull String[] serviceTypeLabels, long elapsedRealtimeMillis) {
String[] hostName = null;
int port = 0;
if (response.hasServiceRecord()) {
@@ -351,7 +355,7 @@
textEntries,
response.getInterfaceIndex(),
response.getNetwork(),
- now.plusMillis(response.getMinRemainingTtl(now.toEpochMilli())));
+ now.plusMillis(response.getMinRemainingTtl(elapsedRealtimeMillis)));
}
private List<MdnsResponse> getExistingServices() {
@@ -380,8 +384,8 @@
if (existingInfo == null) {
for (MdnsResponse existingResponse : serviceCache.getCachedServices(cacheKey)) {
if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
- final MdnsServiceInfo info =
- buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
+ final MdnsServiceInfo info = buildMdnsServiceInfoFromResponse(
+ existingResponse, serviceTypeLabels, clock.elapsedRealtime());
listener.onServiceNameDiscovered(info, true /* isServiceFromCache */);
listenerInfo.setServiceDiscovered(info.getServiceInstanceName());
if (existingResponse.isComplete()) {
@@ -396,10 +400,7 @@
// Keep tracking the ScheduledFuture for the task so we can cancel it if caller is not
// interested anymore.
final QueryTaskConfig taskConfig = new QueryTaskConfig(
- searchOptions.getQueryMode(),
- searchOptions.onlyUseIpv6OnIpv6OnlyNetworks(),
- searchOptions.numOfQueriesBeforeBackoff(),
- socketKey);
+ searchOptions.getQueryMode());
final long now = clock.elapsedRealtime();
if (lastSentTime == 0) {
lastSentTime = now;
@@ -412,7 +413,9 @@
minRemainingTtl,
now,
lastSentTime,
- currentSessionId
+ currentSessionId,
+ searchOptions.getQueryMode(),
+ searchOptions.numOfQueriesBeforeBackoff()
);
dependencies.sendMessageDelayed(
handler,
@@ -424,7 +427,8 @@
mdnsQueryScheduler.scheduleFirstRun(taskConfig, now,
minRemainingTtl, currentSessionId), servicesToResolve,
getAllDiscoverySubtypes(), needSendDiscoveryQueries(listeners),
- getExistingServices());
+ getExistingServices(), searchOptions.onlyUseIpv6OnIpv6OnlyNetworks(),
+ socketKey);
executor.submit(queryTask);
}
@@ -458,7 +462,7 @@
@NonNull MdnsSearchOptions options) {
final boolean matchesInstanceName = options.getResolveInstanceName() == null
// DNS is case-insensitive, so ignore case in the comparison
- || MdnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(),
+ || DnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(),
response.getServiceInstanceName());
// If discovery is requiring some subtypes, the response must have one that matches a
@@ -468,7 +472,7 @@
final boolean matchesSubtype = options.getSubtypes().size() == 0
|| CollectionUtils.any(options.getSubtypes(), requiredSub ->
CollectionUtils.any(responseSubtypes, actualSub ->
- MdnsUtils.equalsIgnoreDnsCase(
+ DnsUtils.equalsIgnoreDnsCase(
MdnsConstants.SUBTYPE_PREFIX + requiredSub, actualSub)));
return matchesInstanceName && matchesSubtype;
@@ -535,7 +539,8 @@
final long minRemainingTtl = getMinRemainingTtl(now);
MdnsQueryScheduler.ScheduledQueryTaskArgs args =
mdnsQueryScheduler.maybeRescheduleCurrentRun(now, minRemainingTtl,
- lastSentTime, currentSessionId + 1);
+ lastSentTime, currentSessionId + 1,
+ searchOptions.numOfQueriesBeforeBackoff());
if (args != null) {
removeScheduledTask();
dependencies.sendMessageDelayed(
@@ -561,7 +566,7 @@
if (response.getServiceInstanceName() != null) {
listeners.valueAt(i).unsetServiceDiscovered(response.getServiceInstanceName());
final MdnsServiceInfo serviceInfo = buildMdnsServiceInfoFromResponse(
- response, serviceTypeLabels);
+ response, serviceTypeLabels, clock.elapsedRealtime());
if (response.isComplete()) {
sharedLog.log(message + ". onServiceRemoved: " + serviceInfo);
listener.onServiceRemoved(serviceInfo);
@@ -605,8 +610,8 @@
+ " %b, responseIsComplete: %b",
serviceInstanceName, newInCache, serviceBecomesComplete,
response.isComplete()));
- MdnsServiceInfo serviceInfo =
- buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
+ final MdnsServiceInfo serviceInfo = buildMdnsServiceInfoFromResponse(
+ response, serviceTypeLabels, clock.elapsedRealtime());
for (int i = 0; i < listeners.size(); i++) {
// If a service stops matching the options (currently can only happen if it loses a
@@ -658,7 +663,7 @@
continue;
}
if (CollectionUtils.any(resolveResponses,
- r -> MdnsUtils.equalsIgnoreDnsCase(resolveName, r.getServiceInstanceName()))) {
+ r -> DnsUtils.equalsIgnoreDnsCase(resolveName, r.getServiceInstanceName()))) {
continue;
}
MdnsResponse knownResponse =
@@ -723,15 +728,21 @@
private final List<String> subtypes = new ArrayList<>();
private final boolean sendDiscoveryQueries;
private final List<MdnsResponse> existingServices = new ArrayList<>();
+ private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
+ private final SocketKey socketKey;
QueryTask(@NonNull MdnsQueryScheduler.ScheduledQueryTaskArgs taskArgs,
@NonNull Collection<MdnsResponse> servicesToResolve,
@NonNull Collection<String> subtypes, boolean sendDiscoveryQueries,
- @NonNull Collection<MdnsResponse> existingServices) {
+ @NonNull Collection<MdnsResponse> existingServices,
+ boolean onlyUseIpv6OnIpv6OnlyNetworks,
+ @NonNull SocketKey socketKey) {
this.taskArgs = taskArgs;
this.servicesToResolve.addAll(servicesToResolve);
this.subtypes.addAll(subtypes);
this.sendDiscoveryQueries = sendDiscoveryQueries;
this.existingServices.addAll(existingServices);
+ this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
+ this.socketKey = socketKey;
}
@Override
@@ -745,8 +756,8 @@
subtypes,
taskArgs.config.expectUnicastResponse,
taskArgs.config.transactionId,
- taskArgs.config.socketKey,
- taskArgs.config.onlyUseIpv6OnIpv6OnlyNetworks,
+ socketKey,
+ onlyUseIpv6OnIpv6OnlyNetworks,
sendDiscoveryQueries,
servicesToResolve,
clock,
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 9cfcba1..17e5b31 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -28,7 +28,9 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -249,6 +251,12 @@
Log.wtf(TAG, "No mDns packets to send");
return;
}
+ // Check all packets with the same address
+ if (!MdnsUtils.checkAllPacketsWithSameAddress(packets)) {
+ Log.wtf(TAG, "Some mDNS packets have a different target address. addresses="
+ + CollectionUtils.map(packets, DatagramPacket::getSocketAddress));
+ return;
+ }
final boolean isIpv4 = ((InetSocketAddress) packets.get(0).getSocketAddress())
.getAddress() instanceof Inet4Address;
diff --git a/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
index 0894166..d2cd463 100644
--- a/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
+++ b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
@@ -19,9 +19,6 @@
import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -51,9 +48,6 @@
static final int MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS = 60000;
private final boolean alwaysAskForUnicastResponse =
MdnsConfigs.alwaysAskForUnicastResponseInEachBurst();
- private final int queryMode;
- final boolean onlyUseIpv6OnIpv6OnlyNetworks;
- private final int numOfQueriesBeforeBackoff;
@VisibleForTesting
final int transactionId;
@VisibleForTesting
@@ -64,16 +58,11 @@
final long delayUntilNextTaskWithoutBackoffMs;
private final boolean isFirstBurst;
private final long queryCount;
- @NonNull
- final SocketKey socketKey;
- QueryTaskConfig(@NonNull QueryTaskConfig other, long queryCount, int transactionId,
+ QueryTaskConfig(long queryCount, int transactionId,
boolean expectUnicastResponse, boolean isFirstBurst, int burstCounter,
int queriesPerBurst, int timeBetweenBurstsInMs,
long delayUntilNextTaskWithoutBackoffMs) {
- this.queryMode = other.queryMode;
- this.onlyUseIpv6OnIpv6OnlyNetworks = other.onlyUseIpv6OnIpv6OnlyNetworks;
- this.numOfQueriesBeforeBackoff = other.numOfQueriesBeforeBackoff;
this.transactionId = transactionId;
this.expectUnicastResponse = expectUnicastResponse;
this.queriesPerBurst = queriesPerBurst;
@@ -82,27 +71,20 @@
this.delayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
this.isFirstBurst = isFirstBurst;
this.queryCount = queryCount;
- this.socketKey = other.socketKey;
}
- QueryTaskConfig(int queryMode,
- boolean onlyUseIpv6OnIpv6OnlyNetworks,
- int numOfQueriesBeforeBackoff,
- @Nullable SocketKey socketKey) {
- this.queryMode = queryMode;
- this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
- this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff;
+ QueryTaskConfig(int queryMode) {
this.queriesPerBurst = QUERIES_PER_BURST;
this.burstCounter = 0;
this.transactionId = 1;
this.expectUnicastResponse = true;
this.isFirstBurst = true;
// Config the scan frequency based on the scan mode.
- if (this.queryMode == AGGRESSIVE_QUERY_MODE) {
+ if (queryMode == AGGRESSIVE_QUERY_MODE) {
this.timeBetweenBurstsInMs = INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS;
this.delayUntilNextTaskWithoutBackoffMs =
TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS;
- } else if (this.queryMode == PASSIVE_QUERY_MODE) {
+ } else if (queryMode == PASSIVE_QUERY_MODE) {
// In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
// in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
// queries.
@@ -116,12 +98,11 @@
this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
}
- this.socketKey = socketKey;
this.queryCount = 0;
}
long getDelayUntilNextTaskWithoutBackoff(boolean isFirstQueryInBurst,
- boolean isLastQueryInBurst) {
+ boolean isLastQueryInBurst, int queryMode) {
if (isFirstQueryInBurst && queryMode == AGGRESSIVE_QUERY_MODE) {
return 0;
}
@@ -133,7 +114,7 @@
: TIME_BETWEEN_QUERIES_IN_BURST_MS;
}
- boolean getNextExpectUnicastResponse(boolean isLastQueryInBurst) {
+ boolean getNextExpectUnicastResponse(boolean isLastQueryInBurst, int queryMode) {
if (!isLastQueryInBurst) {
return false;
}
@@ -143,7 +124,7 @@
return alwaysAskForUnicastResponse;
}
- int getNextTimeBetweenBurstsMs(boolean isLastQueryInBurst) {
+ int getNextTimeBetweenBurstsMs(boolean isLastQueryInBurst, int queryMode) {
if (!isLastQueryInBurst) {
return timeBetweenBurstsInMs;
}
@@ -155,7 +136,7 @@
/**
* Get new QueryTaskConfig for next run.
*/
- public QueryTaskConfig getConfigForNextRun() {
+ public QueryTaskConfig getConfigForNextRun(int queryMode) {
long newQueryCount = queryCount + 1;
int newTransactionId = transactionId + 1;
if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
@@ -177,16 +158,18 @@
}
}
- return new QueryTaskConfig(this, newQueryCount, newTransactionId,
- getNextExpectUnicastResponse(isLastQueryInBurst), newIsFirstBurst, newBurstCounter,
- newQueriesPerBurst, getNextTimeBetweenBurstsMs(isLastQueryInBurst),
- getDelayUntilNextTaskWithoutBackoff(isFirstQueryInBurst, isLastQueryInBurst));
+ return new QueryTaskConfig(newQueryCount, newTransactionId,
+ getNextExpectUnicastResponse(isLastQueryInBurst, queryMode), newIsFirstBurst,
+ newBurstCounter, newQueriesPerBurst,
+ getNextTimeBetweenBurstsMs(isLastQueryInBurst, queryMode),
+ getDelayUntilNextTaskWithoutBackoff(
+ isFirstQueryInBurst, isLastQueryInBurst, queryMode));
}
/**
* Determine if the query backoff should be used.
*/
- public boolean shouldUseQueryBackoff() {
+ public boolean shouldUseQueryBackoff(int numOfQueriesBeforeBackoff) {
// Don't enable backoff mode during the burst or in the first burst
if (burstCounter != 0 || isFirstBurst) {
return false;
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
index 3c11a24..8745941 100644
--- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns.util;
+import static com.android.net.module.util.DnsUtils.equalsDnsLabelIgnoreDnsCase;
+import static com.android.net.module.util.DnsUtils.equalsIgnoreDnsCase;
import static com.android.server.connectivity.mdns.MdnsConstants.FLAG_TRUNCATED;
import android.annotation.NonNull;
@@ -34,6 +36,7 @@
import java.io.IOException;
import java.net.DatagramPacket;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -55,18 +58,17 @@
private MdnsUtils() { }
/**
- * Convert the string to DNS case-insensitive lowercase
+ * Compare labels a equals b or a is suffix of b.
*
- * Per rfc6762#page-46, accented characters are not defined to be automatically equivalent to
- * their unaccented counterparts. So the "DNS lowercase" should be if character is A-Z then they
- * transform into a-z. Otherwise, they are kept as-is.
+ * @param a the type or subtype.
+ * @param b the base type
*/
- public static String toDnsLowerCase(@NonNull String string) {
- final char[] outChars = new char[string.length()];
- for (int i = 0; i < string.length(); i++) {
- outChars[i] = toDnsLowerCase(string.charAt(i));
- }
- return new String(outChars);
+ public static boolean typeEqualsOrIsSubtype(@NonNull String[] a,
+ @NonNull String[] b) {
+ return equalsDnsLabelIgnoreDnsCase(a, b)
+ || ((b.length == (a.length + 2))
+ && equalsIgnoreDnsCase(b[1], MdnsConstants.SUBTYPE_LABEL)
+ && MdnsRecord.labelsAreSuffix(a, b));
}
/**
@@ -80,70 +82,6 @@
}
}
- /**
- * Convert the array of labels to DNS case-insensitive lowercase.
- */
- public static String[] toDnsLabelsLowerCase(@NonNull String[] labels) {
- final String[] outStrings = new String[labels.length];
- for (int i = 0; i < labels.length; ++i) {
- outStrings[i] = toDnsLowerCase(labels[i]);
- }
- return outStrings;
- }
-
- /**
- * Compare two strings by DNS case-insensitive lowercase.
- */
- public static boolean equalsIgnoreDnsCase(@Nullable String a, @Nullable String b) {
- if (a == null || b == null) {
- return a == null && b == null;
- }
- if (a.length() != b.length()) return false;
- for (int i = 0; i < a.length(); i++) {
- if (toDnsLowerCase(a.charAt(i)) != toDnsLowerCase(b.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compare two set of DNS labels by DNS case-insensitive lowercase.
- */
- public static boolean equalsDnsLabelIgnoreDnsCase(@NonNull String[] a, @NonNull String[] b) {
- if (a == b) {
- return true;
- }
- int length = a.length;
- if (b.length != length) {
- return false;
- }
- for (int i = 0; i < length; i++) {
- if (!equalsIgnoreDnsCase(a[i], b[i])) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compare labels a equals b or a is suffix of b.
- *
- * @param a the type or subtype.
- * @param b the base type
- */
- public static boolean typeEqualsOrIsSubtype(@NonNull String[] a,
- @NonNull String[] b) {
- return MdnsUtils.equalsDnsLabelIgnoreDnsCase(a, b)
- || ((b.length == (a.length + 2))
- && MdnsUtils.equalsIgnoreDnsCase(b[1], MdnsConstants.SUBTYPE_LABEL)
- && MdnsRecord.labelsAreSuffix(a, b));
- }
-
- private static char toDnsLowerCase(char a) {
- return a >= 'A' && a <= 'Z' ? (char) (a + ('a' - 'A')) : a;
- }
-
/*** Ensure that current running thread is same as given handler thread */
public static void ensureRunningOnHandlerThread(@NonNull Handler handler) {
if (!isRunningOnHandlerThread(handler)) {
@@ -361,4 +299,23 @@
return SystemClock.elapsedRealtime();
}
}
+
+ /**
+ * Check all DatagramPackets with the same destination address.
+ */
+ public static boolean checkAllPacketsWithSameAddress(List<DatagramPacket> packets) {
+ // No packet for address check
+ if (packets.isEmpty()) {
+ return true;
+ }
+
+ final InetAddress address =
+ ((InetSocketAddress) packets.get(0).getSocketAddress()).getAddress();
+ for (DatagramPacket packet : packets) {
+ if (!address.equals(((InetSocketAddress) packet.getSocketAddress()).getAddress())) {
+ return false;
+ }
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/service/Android.bp b/service/Android.bp
index 1dd09a9..1a0e045 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -190,18 +190,12 @@
"connectivity_native_aidl_interface-lateststable-java",
"dnsresolver_aidl_interface-V15-java",
"modules-utils-shell-command-handler",
- "net-utils-device-common",
- "net-utils-device-common-ip",
- "net-utils-device-common-netlink",
- "net-utils-services-common",
+ "net-utils-service-connectivity",
"netd-client",
"networkstack-client",
"PlatformProperties",
"service-connectivity-protos",
"service-connectivity-stats-protos",
- // The required dependency net-utils-device-common-struct-base is in the classpath via
- // framework-connectivity
- "net-utils-device-common-struct",
],
apex_available: [
"com.android.tethering",
diff --git a/service/ServiceConnectivityResources/res/values/config_thread.xml b/service/ServiceConnectivityResources/res/values/config_thread.xml
index 02a9ce6..4027038 100644
--- a/service/ServiceConnectivityResources/res/values/config_thread.xml
+++ b/service/ServiceConnectivityResources/res/values/config_thread.xml
@@ -54,9 +54,21 @@
-->
<string translatable="false" name="config_thread_model_name">Thread Border Router</string>
- <!-- Whether the Thread network will be managed by the Google Home ecosystem. When this value
- is set, a TXT entry "vgh=0" or "vgh=1" will be added to the "_mehscop._udp" mDNS service
- respectively (The TXT value is a string).
+ <!-- Specifies vendor-specific mDNS TXT entries which will be included in the "_meshcop._udp"
+ service. The TXT entries list MUST conform to the format requirement in RFC 6763 section 6. For
+ example, the key and value of each TXT entry MUST be separated with "=". If the value length is
+ 0, the trailing "=" may be omitted. Additionally, the TXT keys MUST start with "v" and be at
+ least 2 characters.
+
+ Note, do not include credentials in any of the TXT entries - they will be advertised on Wi-Fi
+ or Ethernet link.
+
+ An example config can be:
+ <string-array name="config_thread_mdns_vendor_specific_txts">
+ <item>vab=123</item>
+ <item>vcd</item>
+ </string-array>
-->
- <bool name="config_thread_managed_by_google_home">false</bool>
+ <string-array name="config_thread_mdns_vendor_specific_txts">
+ </string-array>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 158b0c8..fbaae05 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -51,6 +51,7 @@
<item type="string" name="config_thread_vendor_name" />
<item type="string" name="config_thread_vendor_oui" />
<item type="string" name="config_thread_model_name" />
+ <item type="array" name="config_thread_mdns_vendor_specific_txts" />
</policy>
</overlayable>
</resources>
diff --git a/service/src/com/android/server/CallbackQueue.java b/service/src/com/android/server/CallbackQueue.java
new file mode 100644
index 0000000..060a984
--- /dev/null
+++ b/service/src/com/android/server/CallbackQueue.java
@@ -0,0 +1,126 @@
+/*
+ * 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.annotation.NonNull;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+
+import com.android.net.module.util.GrowingIntArray;
+
+/**
+ * A utility class to add/remove {@link NetworkCallback}s from a queue.
+ *
+ * <p>This is intended to be used as a temporary builder to create/modify callbacks stored in an int
+ * array for memory efficiency.
+ *
+ * <p>Intended usage:
+ * <pre>
+ * final CallbackQueue queue = new CallbackQueue(storedCallbacks);
+ * queue.forEach(netId, callbackId -> { [...] });
+ * queue.addCallback(netId, callbackId);
+ * [...]
+ * queue.shrinkToLength();
+ * storedCallbacks = queue.getBackingArray();
+ * </pre>
+ *
+ * <p>This class is not thread-safe.
+ */
+public class CallbackQueue extends GrowingIntArray {
+ public CallbackQueue(int[] initialCallbacks) {
+ super(initialCallbacks);
+ }
+
+ /**
+ * Get a callback int from netId and callbackId.
+ *
+ * <p>The first 16 bits of each int is the netId; the last 16 bits are the callback index.
+ */
+ private static int getCallbackInt(int netId, int callbackId) {
+ return (netId << 16) | (callbackId & 0xffff);
+ }
+
+ private static int getNetId(int callbackInt) {
+ return callbackInt >>> 16;
+ }
+
+ private static int getCallbackId(int callbackInt) {
+ return callbackInt & 0xffff;
+ }
+
+ /**
+ * A consumer interface for {@link #forEach(CallbackConsumer)}.
+ *
+ * <p>This is similar to a BiConsumer<Integer, Integer>, but avoids the boxing cost.
+ */
+ public interface CallbackConsumer {
+ /**
+ * Method called on each callback in the queue.
+ */
+ void accept(int netId, int callbackId);
+ }
+
+ /**
+ * Iterate over all callbacks in the queue.
+ */
+ public void forEach(@NonNull CallbackConsumer consumer) {
+ forEach(value -> {
+ final int netId = getNetId(value);
+ final int callbackId = getCallbackId(value);
+ consumer.accept(netId, callbackId);
+ });
+ }
+
+ /**
+ * Indicates whether the queue contains a callback for the given (netId, callbackId).
+ */
+ public boolean hasCallback(int netId, int callbackId) {
+ return contains(getCallbackInt(netId, callbackId));
+ }
+
+ /**
+ * Remove all callbacks for the given netId.
+ *
+ * @return true if at least one callback was removed.
+ */
+ public boolean removeCallbacksForNetId(int netId) {
+ return removeValues(cb -> getNetId(cb) == netId);
+ }
+
+ /**
+ * Remove all callbacks for the given netId and callbackId.
+ * @return true if at least one callback was removed.
+ */
+ public boolean removeCallbacks(int netId, int callbackId) {
+ final int cbInt = getCallbackInt(netId, callbackId);
+ return removeValues(cb -> cb == cbInt);
+ }
+
+ /**
+ * Add a callback at the end of the queue.
+ */
+ public void addCallback(int netId, int callbackId) {
+ add(getCallbackInt(netId, callbackId));
+ }
+
+ @Override
+ protected String valueToString(int item) {
+ final int callbackId = getCallbackId(item);
+ final int netId = getNetId(item);
+ return ConnectivityManager.getCallbackName(callbackId) + "(" + netId + ")";
+ }
+}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 2a3058c..953fd76 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -38,14 +38,25 @@
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND;
import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
-import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.BLOCKED_REASON_NETWORK_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
+import static android.net.ConnectivityManager.CALLBACK_AVAILABLE;
+import static android.net.ConnectivityManager.CALLBACK_BLK_CHANGED;
+import static android.net.ConnectivityManager.CALLBACK_CAP_CHANGED;
import static android.net.ConnectivityManager.CALLBACK_IP_CHANGED;
+import static android.net.ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED;
+import static android.net.ConnectivityManager.CALLBACK_LOSING;
+import static android.net.ConnectivityManager.CALLBACK_LOST;
+import static android.net.ConnectivityManager.CALLBACK_PRECHECK;
+import static android.net.ConnectivityManager.CALLBACK_RESUMED;
+import static android.net.ConnectivityManager.CALLBACK_SUSPENDED;
+import static android.net.ConnectivityManager.CALLBACK_UNAVAIL;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
+import static android.net.ConnectivityManager.NetworkCallback.DECLARED_METHODS_ALL;
import static android.net.ConnectivityManager.NetworkCallback.DECLARED_METHODS_NONE;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -66,7 +77,6 @@
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
-import static android.net.ConnectivityManager.NetworkCallback.DECLARED_METHODS_ALL;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
@@ -135,8 +145,9 @@
import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf;
import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
import static com.android.server.connectivity.ConnectivityFlags.DELAY_DESTROY_SOCKETS;
-import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
+import static com.android.server.connectivity.ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS;
+import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
import android.Manifest;
import android.annotation.CheckResult;
@@ -321,6 +332,7 @@
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.PerUidCounter;
import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.RoutingCoordinatorService;
import com.android.net.module.util.TcUtils;
import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.networkstack.apishim.BroadcastOptionsShimImpl;
@@ -358,7 +370,6 @@
import com.android.server.connectivity.ProfileNetworkPreferenceInfo;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
-import com.android.server.connectivity.RoutingCoordinatorService;
import com.android.server.connectivity.SatelliteAccessController;
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.VpnNetworkPreferenceInfo;
@@ -500,6 +511,9 @@
private final boolean mUseDeclaredMethodsForCallbacksEnabled;
+ // Flag to delay callbacks for frozen apps, suppressing duplicate and stale callbacks.
+ private final boolean mQueueCallbacksForFrozenApps;
+
/**
* Uids ConnectivityService tracks blocked status of to send blocked status callbacks.
* Key is uid based on mAsUid of registered networkRequestInfo
@@ -1863,6 +1877,9 @@
context, ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN);
mUseDeclaredMethodsForCallbacksEnabled = mDeps.isFeatureEnabled(context,
ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS);
+ // registerUidFrozenStateChangedCallback is only available on U+
+ mQueueCallbacksForFrozenApps = mDeps.isAtLeastU()
+ && mDeps.isFeatureEnabled(context, QUEUE_CALLBACKS_FOR_FROZEN_APPS);
mCarrierPrivilegeAuthenticator = mDeps.makeCarrierPrivilegeAuthenticator(
mContext, mTelephonyManager, mRequestRestrictedWifiEnabled,
this::handleUidCarrierPrivilegesLost, mHandler);
@@ -2018,7 +2035,7 @@
mDelayDestroySockets = mDeps.isFeatureNotChickenedOut(context, DELAY_DESTROY_SOCKETS);
mAllowSysUiConnectivityReports = mDeps.isFeatureNotChickenedOut(
mContext, ALLOW_SYSUI_CONNECTIVITY_REPORTS);
- if (mDestroyFrozenSockets) {
+ if (mDestroyFrozenSockets || mQueueCallbacksForFrozenApps) {
final UidFrozenStateChangedCallback frozenStateChangedCallback =
new UidFrozenStateChangedCallback() {
@Override
@@ -3454,6 +3471,14 @@
private void handleFrozenUids(int[] uids, int[] frozenStates) {
ensureRunningOnConnectivityServiceThread();
+ handleDestroyFrozenSockets(uids, frozenStates);
+ // TODO: handle freezing NetworkCallbacks
+ }
+
+ private void handleDestroyFrozenSockets(int[] uids, int[] frozenStates) {
+ if (!mDestroyFrozenSockets) {
+ return;
+ }
for (int i = 0; i < uids.length; i++) {
final int uid = uids[i];
final boolean addReason = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
@@ -5364,7 +5389,7 @@
// by other networks that are already connected. Perhaps that can be done by
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
+ notifyNetworkCallbacks(nai, CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK);
mQosCallbackTracker.handleNetworkReleased(nai.network);
@@ -5486,8 +5511,7 @@
// correctly contains null as an upstream.
if (sendCallbacks) {
nri.setSatisfier(null, null);
- notifyNetworkCallbacks(local,
- ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
+ notifyNetworkCallbacks(local, CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
}
}
@@ -5862,8 +5886,7 @@
log("releasing " + nri.mRequests.get(0) + " (timeout)");
}
handleRemoveNetworkRequest(nri);
- callCallbackForRequest(
- nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ callCallbackForRequest(nri, null, CALLBACK_UNAVAIL, 0);
}
private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
@@ -5879,7 +5902,7 @@
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ callCallbackForRequest(nri, null, CALLBACK_UNAVAIL, 0);
}
}
@@ -7035,7 +7058,7 @@
// should have its link properties fixed up for PAC proxies.
mProxyTracker.updateDefaultNetworkProxyPortForPAC(nai.linkProperties, nai.network);
if (nai.everConnected()) {
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_IP_CHANGED);
+ notifyNetworkCallbacks(nai, CALLBACK_IP_CHANGED);
}
}
@@ -7756,6 +7779,11 @@
}
}
+ boolean isCallbackOverridden(int callbackId) {
+ return !mUseDeclaredMethodsForCallbacksEnabled
+ || (mDeclaredMethodsFlags & (1 << callbackId)) != 0;
+ }
+
boolean hasHigherOrderThan(@NonNull final NetworkRequestInfo target) {
// Compare two preference orders.
return mPreferenceOrder < target.mPreferenceOrder;
@@ -7792,10 +7820,52 @@
+ " callback flags: " + mCallbackFlags
+ " order: " + mPreferenceOrder
+ " isUidTracked: " + mUidTrackedForBlockedStatus
- + " declaredMethods: 0x" + Integer.toHexString(mDeclaredMethodsFlags);
+ + " declaredMethods: " + declaredMethodsFlagsToString(mDeclaredMethodsFlags);
}
}
+ /**
+ * Get a readable String for a bitmask of declared methods.
+ */
+ @VisibleForTesting
+ public static String declaredMethodsFlagsToString(int flags) {
+ if (flags == DECLARED_METHODS_NONE) {
+ return "NONE";
+ }
+ if (flags == DECLARED_METHODS_ALL) {
+ return "ALL";
+ }
+ final StringBuilder sb = new StringBuilder();
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_PRECHECK, "PRECHK", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_AVAILABLE, "AVAIL", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_LOSING, "LOSING", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_LOST, "LOST", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_UNAVAIL, "UNAVAIL", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_CAP_CHANGED, "NC", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_IP_CHANGED, "LP", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_SUSPENDED, "SUSP", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_RESUMED, "RESUME", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_BLK_CHANGED, "BLK", sb);
+ flags = maybeAppendDeclaredMethod(flags, CALLBACK_LOCAL_NETWORK_INFO_CHANGED,
+ "LOCALINF", sb);
+ if (flags != 0) {
+ sb.append("|0x").append(Integer.toHexString(flags));
+ }
+ return sb.toString();
+ }
+
+ private static int maybeAppendDeclaredMethod(int declaredMethodsFlags,
+ int callbackId, String callbackName, @NonNull StringBuilder builder) {
+ final int callbackFlag = 1 << callbackId;
+ if ((declaredMethodsFlags & callbackFlag) != 0) {
+ if (builder.length() > 0) {
+ builder.append('|');
+ }
+ builder.append(callbackName);
+ }
+ return declaredMethodsFlags & ~callbackFlag;
+ }
+
// Keep backward compatibility since the ServiceSpecificException is used by
// the API surface, see {@link ConnectivityManager#convertServiceException}.
public static class RequestInfoPerUidCounter extends PerUidCounter {
@@ -8209,21 +8279,13 @@
// Policy already enforced.
return;
}
- if (mDeps.isAtLeastV()) {
- if (mBpfNetMaps.isUidRestrictedOnMeteredNetworks(uid)) {
- // If UID is restricted, don't allow them to bring up metered APNs.
- networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
- }
- return;
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mPolicyManager.isUidRestrictedOnMeteredNetworks(uid)) {
- // If UID is restricted, don't allow them to bring up metered APNs.
- networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
+ final boolean isRestrictedOnMeteredNetworks = mDeps.isAtLeastV()
+ ? mBpfNetMaps.isUidRestrictedOnMeteredNetworks(uid)
+ : BinderUtils.withCleanCallingIdentity(() ->
+ mPolicyManager.isUidRestrictedOnMeteredNetworks(uid));
+ if (isRestrictedOnMeteredNetworks) {
+ // If UID is restricted, don't allow them to bring up metered APNs.
+ networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
}
}
@@ -9090,7 +9152,7 @@
}
networkAgent.networkMonitor().notifyLinkPropertiesChanged(
new LinkProperties(newLp, true /* parcelSensitiveFields */));
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+ notifyNetworkCallbacks(networkAgent, CALLBACK_IP_CHANGED);
}
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -9586,8 +9648,7 @@
if (prevSuspended != suspended) {
// TODO (b/73132094) : remove this call once the few users of onSuspended and
// onResumed have been removed.
- notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
- : ConnectivityManager.CALLBACK_RESUMED);
+ notifyNetworkCallbacks(nai, suspended ? CALLBACK_SUSPENDED : CALLBACK_RESUMED);
}
if (prevSuspended != suspended || prevRoaming != roaming) {
// updateNetworkInfo will mix in the suspended info from the capabilities and
@@ -9674,7 +9735,7 @@
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests();
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ notifyNetworkCallbacks(nai, CALLBACK_CAP_CHANGED);
}
updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
@@ -9816,7 +9877,7 @@
// But here there is no new request, so the rematch won't see anything. Send
// callbacks to apps now to tell them about the loss of upstream.
notifyNetworkCallbacks(nai,
- ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
+ CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
return;
}
}
@@ -10122,7 +10183,7 @@
private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
int notificationType) {
- if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
+ if (notificationType == CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
// If apps could file multi-layer requests with PendingIntents, they'd need to know
@@ -10192,6 +10253,18 @@
return new LocalNetworkInfo.Builder().setUpstreamNetwork(upstream).build();
}
+ private Bundle makeCommonBundleForCallback(@NonNull final NetworkRequestInfo nri,
+ @Nullable Network network) {
+ final Bundle bundle = new Bundle();
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
+ // TODO: check if defensive copies of data is needed.
+ putParcelable(bundle, nri.getNetworkRequestForCallback());
+ if (network != null) {
+ putParcelable(bundle, network);
+ }
+ return bundle;
+ }
+
// networkAgent is only allowed to be null if notificationType is
// CALLBACK_UNAVAIL. This is because UNAVAIL is about no network being
// available, while all other cases are about some particular network.
@@ -10204,24 +10277,19 @@
// are Type.LISTEN, but should not have NetworkCallbacks invoked.
return;
}
- if (mUseDeclaredMethodsForCallbacksEnabled
- && (nri.mDeclaredMethodsFlags & (1 << notificationType)) == 0) {
+ if (!nri.isCallbackOverridden(notificationType)) {
// No need to send the notification as the recipient method is not overridden
return;
}
- final Bundle bundle = new Bundle();
- // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
- // TODO: check if defensive copies of data is needed.
- final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
- putParcelable(bundle, nrForCallback);
- Message msg = Message.obtain();
- if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
- putParcelable(bundle, networkAgent.network);
- }
+ final Network bundleNetwork = notificationType == CALLBACK_UNAVAIL
+ ? null
+ : networkAgent.network;
+ final Bundle bundle = makeCommonBundleForCallback(nri, bundleNetwork);
final boolean includeLocationSensitiveInfo =
(nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
+ final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
switch (notificationType) {
- case ConnectivityManager.CALLBACK_AVAILABLE: {
+ case CALLBACK_AVAILABLE: {
final NetworkCapabilities nc =
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
networkCapabilitiesRestrictedForCallerPermissions(
@@ -10236,15 +10304,9 @@
// method here.
bundle.putParcelable(LocalNetworkInfo.class.getSimpleName(),
localNetworkInfoForNai(networkAgent));
- // For this notification, arg1 contains the blocked status.
- msg.arg1 = arg1;
break;
}
- case ConnectivityManager.CALLBACK_LOSING: {
- msg.arg1 = arg1;
- break;
- }
- case ConnectivityManager.CALLBACK_CAP_CHANGED: {
+ case CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
final NetworkCapabilities netCap =
networkCapabilitiesRestrictedForCallerPermissions(
@@ -10257,17 +10319,16 @@
nri.mCallingAttributionTag));
break;
}
- case ConnectivityManager.CALLBACK_IP_CHANGED: {
+ case CALLBACK_IP_CHANGED: {
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
break;
}
- case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+ case CALLBACK_BLK_CHANGED: {
maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1);
- msg.arg1 = arg1;
break;
}
- case ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED: {
+ case CALLBACK_LOCAL_NETWORK_INFO_CHANGED: {
if (!networkAgent.isLocalNetwork()) {
Log.wtf(TAG, "Callback for local info for a non-local network");
return;
@@ -10276,17 +10337,26 @@
break;
}
}
+ callCallbackForRequest(nri, notificationType, bundle, arg1);
+ }
+
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri, int notificationType,
+ Bundle bundle, int arg1) {
+ Message msg = Message.obtain();
+ msg.arg1 = arg1;
msg.what = notificationType;
msg.setData(bundle);
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nrForCallback);
+ log("sending notification " + notification + " for "
+ + nri.getNetworkRequestForCallback());
}
nri.mMessenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
+ loge("RemoteException caught trying to send a callback msg for "
+ + nri.getNetworkRequestForCallback());
}
}
@@ -10536,7 +10606,7 @@
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
// For consistency with previous behaviour, send onLost callbacks before onAvailable.
processNewlyLostListenRequests(nai);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ notifyNetworkCallbacks(nai, CALLBACK_CAP_CHANGED);
processNewlySatisfiedListenRequests(nai);
}
@@ -10549,7 +10619,7 @@
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
nai.removeRequest(nr.requestId);
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
+ callCallbackForRequest(nri, nai, CALLBACK_LOST, 0);
}
}
}
@@ -10881,7 +10951,7 @@
notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
- ConnectivityManager.CALLBACK_LOST, 0);
+ CALLBACK_LOST, 0);
}
}
@@ -10925,7 +10995,7 @@
if (null != localInfoChangedAgents) {
for (final NetworkAgentInfo nai : localInfoChangedAgents) {
notifyNetworkCallbacks(nai,
- ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
+ CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
}
}
@@ -10968,7 +11038,7 @@
if (Objects.equals(nai.networkCapabilities, newNc)) return;
updateNetworkPermissions(nai, newNc);
nai.getAndSetNetworkCapabilities(newNc);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ notifyNetworkCallbacks(nai, CALLBACK_CAP_CHANGED);
}
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
@@ -11337,7 +11407,7 @@
rematchAllNetworksAndRequests();
// This has to happen after matching the requests, because callbacks are just requests.
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
+ notifyNetworkCallbacks(networkAgent, CALLBACK_PRECHECK);
} else if (state == NetworkInfo.State.DISCONNECTED) {
networkAgent.disconnect();
if (networkAgent.isVPN()) {
@@ -11370,22 +11440,18 @@
protected void notifyNetworkAvailable(NetworkAgentInfo nai, NetworkRequestInfo nri) {
mHandler.removeMessages(EVENT_TIMEOUT_NETWORK_REQUEST, nri);
if (nri.mPendingIntent != null) {
- sendPendingIntentForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE);
+ sendPendingIntentForRequest(nri, nai, CALLBACK_AVAILABLE);
// Attempt no subsequent state pushes where intents are involved.
return;
}
- final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
- final boolean metered = nai.networkCapabilities.isMetered();
- final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE,
- getBlockedState(nri.mAsUid, blockedReasons, metered, vpnBlocked));
+ callCallbackForRequest(nri, nai, CALLBACK_AVAILABLE, getBlockedState(nai, nri.mAsUid));
}
// Notify the requests on this NAI that the network is now lingered.
private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
final int lingerTime = (int) (nai.getInactivityExpiry() - now);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+ notifyNetworkCallbacks(nai, CALLBACK_LOSING, lingerTime);
}
private int getPermissionBlockedState(final int uid, final int reasons) {
@@ -11409,6 +11475,13 @@
: reasons & ~BLOCKED_REASON_LOCKDOWN_VPN;
}
+ private int getBlockedState(@NonNull NetworkAgentInfo nai, int uid) {
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
+ final int blockedReasons = mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE);
+ return getBlockedState(uid, blockedReasons, metered, vpnBlocked);
+ }
+
private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) {
if (blockedReasons == BLOCKED_REASON_NONE) {
mUidBlockedReasons.delete(uid);
@@ -11448,7 +11521,7 @@
final int newBlockedState = getBlockedState(
nri.mAsUid, blockedReasons, newMetered, newVpnBlocked);
if (oldBlockedState != newBlockedState) {
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ callCallbackForRequest(nri, nai, CALLBACK_BLK_CHANGED,
newBlockedState);
}
}
@@ -11475,7 +11548,7 @@
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (nri != null && nri.mAsUid == uid) {
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ callCallbackForRequest(nri, nai, CALLBACK_BLK_CHANGED,
newBlockedState);
}
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 1ee1ed7..df87316 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -49,6 +49,9 @@
public static final String USE_DECLARED_METHODS_FOR_CALLBACKS =
"use_declared_methods_for_callbacks";
+ public static final String QUEUE_CALLBACKS_FOR_FROZEN_APPS =
+ "queue_callbacks_for_frozen_apps";
+
private boolean mNoRematchAllRequestsOnRegister;
/**
diff --git a/service/src/com/android/server/connectivity/ConnectivityNativeService.java b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
index cf6127f..917ad4d 100644
--- a/service/src/com/android/server/connectivity/ConnectivityNativeService.java
+++ b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
@@ -23,6 +23,7 @@
import android.os.Binder;
import android.os.Process;
import android.os.ServiceSpecificException;
+import android.os.UserHandle;
import android.system.ErrnoException;
import android.util.Log;
@@ -67,8 +68,8 @@
}
private void enforceBlockPortPermission() {
- final int uid = Binder.getCallingUid();
- if (uid == Process.ROOT_UID || uid == Process.PHONE_UID) return;
+ final int appId = UserHandle.getAppId(Binder.getCallingUid());
+ if (appId == Process.ROOT_UID || appId == Process.PHONE_UID) return;
PermissionUtils.enforceNetworkStackPermission(mContext);
}
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index 34ea9ab..de7f2d4 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -58,13 +58,8 @@
"//apex_available:platform",
],
visibility: [
- "//frameworks/base/packages/Tethering",
"//packages/modules/Connectivity:__subpackages__",
"//packages/modules/Connectivity/framework:__subpackages__",
- "//frameworks/opt/net/ike",
- "//frameworks/opt/net/wifi/service",
- "//packages/modules/Wifi/service",
- "//frameworks/opt/net/telephony",
"//packages/modules/NetworkStack:__subpackages__",
"//packages/modules/CaptivePortalLogin",
],
@@ -345,12 +340,10 @@
],
sdk_version: "module_current",
min_sdk_version: "30",
- static_libs: [
- "modules-utils-build_system",
- ],
libs: [
"framework-annotations-lib",
"framework-connectivity",
+ "modules-utils-build_system",
],
// TODO: remove "apex_available:platform".
apex_available: [
@@ -445,6 +438,7 @@
"device/com/android/net/module/util/SharedLog.java",
"framework/com/android/net/module/util/ByteUtils.java",
"framework/com/android/net/module/util/CollectionUtils.java",
+ "framework/com/android/net/module/util/DnsUtils.java",
"framework/com/android/net/module/util/HexDump.java",
"framework/com/android/net/module/util/LinkPropertiesUtils.java",
],
@@ -456,6 +450,128 @@
visibility: ["//packages/modules/Connectivity/service-t"],
}
+java_library {
+ name: "net-utils-framework-connectivity",
+ srcs: [
+ ":net-utils-framework-connectivity-srcs",
+ ],
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ visibility: [
+ "//packages/modules/Connectivity:__subpackages__",
+ "//packages/modules/NetworkStack:__subpackages__",
+ ],
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-annotations-lib",
+ "framework-configinfrastructure",
+ "framework-connectivity.stubs.module_lib",
+ ],
+ lint: {
+ strict_updatability_linting: true,
+ error_checks: ["NewApi"],
+ },
+}
+
+java_defaults {
+ name: "net-utils-non-bootclasspath-defaults",
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-annotations-lib",
+ "framework-configinfrastructure",
+ "framework-connectivity",
+ "framework-connectivity.stubs.module_lib",
+ "framework-connectivity-t.stubs.module_lib",
+ "framework-location.stubs.module_lib",
+ "framework-tethering",
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ "modules-utils-build_system",
+ "modules-utils-statemachine",
+ "net-utils-non-bootclasspath-aidl-java",
+ "netd-client",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ visibility: [
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+ defaults_visibility: [
+ "//visibility:private",
+ ],
+ lint: {
+ strict_updatability_linting: true,
+ error_checks: ["NewApi"],
+ },
+}
+
+java_library {
+ name: "net-utils-service-connectivity",
+ srcs: [
+ ":net-utils-all-srcs",
+ ],
+ exclude_srcs: [
+ ":net-utils-framework-connectivity-srcs",
+ ],
+ libs: [
+ "net-utils-framework-connectivity",
+ ],
+ defaults: ["net-utils-non-bootclasspath-defaults"],
+ jarjar_rules: "jarjar-rules-shared.txt",
+}
+
+java_library {
+ name: "net-utils-tethering",
+ srcs: [
+ ":net-utils-all-srcs",
+ ":framework-connectivity-shared-srcs",
+ ],
+ defaults: ["net-utils-non-bootclasspath-defaults"],
+ jarjar_rules: "jarjar-rules-shared.txt",
+}
+
+aidl_interface {
+ name: "net-utils-non-bootclasspath-aidl",
+ srcs: [
+ ":net-utils-aidl-srcs",
+ ],
+ unstable: true,
+ backend: {
+ java: {
+ enabled: true,
+ min_sdk_version: "30",
+ apex_available: [
+ "com.android.tethering",
+ "com.android.wifi",
+ ],
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ rust: {
+ enabled: false,
+ },
+ },
+ include_dirs: [
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
+ visibility: [
+ "//system/tools/aidl/build",
+ ],
+}
+
// Use a filegroup and not a library for telephony sources, as framework-annotations cannot be
// included either (some annotations would be duplicated on the bootclasspath).
filegroup {
@@ -473,8 +589,7 @@
],
}
-// Use a filegroup and not a library for wifi sources, as this needs corresponding jar-jar
-// rules on the wifi side.
+// Filegroup to build lib used by Wifi framework
// Any class here *must* have a corresponding jarjar rule in the wifi build rules.
filegroup {
name: "net-utils-framework-wifi-common-srcs",
@@ -486,24 +601,78 @@
"framework/com/android/net/module/util/NetUtils.java",
],
path: "framework",
+ visibility: ["//visibility:private"],
+}
+
+// Use a file group containing classes necessary for framework-connectivity. The file group should
+// be as small as possible because because the classes end up in the bootclasspath and R8 is not
+// used to remove unused classes.
+filegroup {
+ name: "net-utils-framework-connectivity-srcs",
+ srcs: [
+ "device/com/android/net/module/util/BpfBitmap.java",
+ "device/com/android/net/module/util/BpfDump.java",
+ "device/com/android/net/module/util/BpfMap.java",
+ "device/com/android/net/module/util/BpfUtils.java",
+ "device/com/android/net/module/util/IBpfMap.java",
+ "device/com/android/net/module/util/JniUtil.java",
+ "device/com/android/net/module/util/SingleWriterBpfMap.java",
+ "device/com/android/net/module/util/Struct.java",
+ "device/com/android/net/module/util/TcUtils.java",
+ "framework/com/android/net/module/util/HexDump.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "net-utils-all-srcs",
+ srcs: [
+ "device/**/*.java",
+ ":net-utils-framework-common-srcs",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "net-utils-aidl-srcs",
+ srcs: [
+ "device/**/*.aidl",
+ ],
+ path: "device",
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "net-utils-service-wifi",
+ srcs: [
+ ":net-utils-all-srcs",
+ ],
+ exclude_srcs: [":net-utils-framework-wifi-common-srcs"],
+ libs: [
+ "net-utils-framework-wifi",
+ ],
+ defaults: ["net-utils-non-bootclasspath-defaults"],
+
visibility: [
- "//frameworks/base",
+ "//packages/modules/Wifi/service",
+ ],
+ apex_available: [
+ "com.android.wifi",
],
}
-// Use a filegroup and not a library for wifi sources, as this needs corresponding jar-jar
-// rules on the wifi side.
-// Any class here *must* have a corresponding jarjar rule in the wifi build rules.
-filegroup {
- name: "net-utils-wifi-service-common-srcs",
- srcs: [
- "device/android/net/NetworkFactory.java",
- "device/android/net/NetworkFactoryImpl.java",
- "device/android/net/NetworkFactoryLegacyImpl.java",
- "device/android/net/NetworkFactoryShim.java",
+java_library {
+ name: "net-utils-framework-wifi",
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ srcs: [":net-utils-framework-wifi-common-srcs"],
+ libs: [
+ "framework-annotations-lib",
+ "framework-connectivity.stubs.module_lib",
+ "unsupportedappusage",
],
visibility: [
- "//frameworks/opt/net/wifi/service",
- "//packages/modules/Wifi/service",
+ "//packages/modules/Wifi/framework",
],
+ apex_available: ["com.android.wifi"],
}
diff --git a/staticlibs/device/com/android/net/module/util/GrowingIntArray.java b/staticlibs/device/com/android/net/module/util/GrowingIntArray.java
new file mode 100644
index 0000000..4a81c10
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/GrowingIntArray.java
@@ -0,0 +1,190 @@
+/*
+ * 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.net.module.util;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.StringJoiner;
+import java.util.function.IntConsumer;
+import java.util.function.IntPredicate;
+
+/**
+ * A growing array of primitive ints.
+ *
+ * <p>This is similar to ArrayList<Integer>, but avoids the cost of boxing (each Integer costs
+ * 16 bytes) and creation / garbage collection of individual Integer objects.
+ *
+ * <p>This class does not use any heuristic for growing capacity, so every call to
+ * {@link #add(int)} may reallocate the backing array. Callers should use
+ * {@link #ensureHasCapacity(int)} to minimize this behavior when they plan to add several values.
+ */
+public class GrowingIntArray {
+ private int[] mValues;
+ private int mLength;
+
+ /**
+ * Create an empty GrowingIntArray with the given capacity.
+ */
+ public GrowingIntArray(int initialCapacity) {
+ mValues = new int[initialCapacity];
+ mLength = 0;
+ }
+
+ /**
+ * Create a GrowingIntArray with an initial array of values.
+ *
+ * <p>The array will be used as-is and may be modified, so callers must stop using it after
+ * calling this constructor.
+ */
+ protected GrowingIntArray(int[] initialValues) {
+ mValues = initialValues;
+ mLength = initialValues.length;
+ }
+
+ /**
+ * Add a value to the array.
+ */
+ public void add(int value) {
+ ensureHasCapacity(1);
+ mValues[mLength] = value;
+ mLength++;
+ }
+
+ /**
+ * Get the current number of values in the array.
+ */
+ public int length() {
+ return mLength;
+ }
+
+ /**
+ * Get the value at a given index.
+ *
+ * @throws ArrayIndexOutOfBoundsException if the index is out of bounds.
+ */
+ public int get(int index) {
+ if (index < 0 || index >= mLength) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return mValues[index];
+ }
+
+ /**
+ * Iterate over all values in the array.
+ */
+ public void forEach(@NonNull IntConsumer consumer) {
+ for (int i = 0; i < mLength; i++) {
+ consumer.accept(mValues[i]);
+ }
+ }
+
+ /**
+ * Remove all values matching a predicate.
+ *
+ * @return true if at least one value was removed.
+ */
+ public boolean removeValues(@NonNull IntPredicate predicate) {
+ int newQueueLength = 0;
+ for (int i = 0; i < mLength; i++) {
+ final int cb = mValues[i];
+ if (!predicate.test(cb)) {
+ mValues[newQueueLength] = cb;
+ newQueueLength++;
+ }
+ }
+ if (mLength != newQueueLength) {
+ mLength = newQueueLength;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Indicates whether the array contains the given value.
+ */
+ public boolean contains(int value) {
+ for (int i = 0; i < mLength; i++) {
+ if (mValues[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove all values from the array.
+ */
+ public void clear() {
+ mLength = 0;
+ }
+
+ /**
+ * Ensure at least the given number of values can be added to the array without reallocating.
+ *
+ * @param capacity The minimum number of additional values the array must be able to hold.
+ */
+ public void ensureHasCapacity(int capacity) {
+ if (mValues.length >= mLength + capacity) {
+ return;
+ }
+ mValues = Arrays.copyOf(mValues, mLength + capacity);
+ }
+
+ @VisibleForTesting
+ int getBackingArrayLength() {
+ return mValues.length;
+ }
+
+ /**
+ * Shrink the array backing this class to the minimum required length.
+ */
+ public void shrinkToLength() {
+ if (mValues.length != mLength) {
+ mValues = Arrays.copyOf(mValues, mLength);
+ }
+ }
+
+ /**
+ * Get values as array by shrinking the internal array to length and returning it.
+ *
+ * <p>This avoids reallocations if the array is already the correct length, but callers should
+ * stop using this instance of {@link GrowingIntArray} if they use the array returned by this
+ * method.
+ */
+ public int[] getShrinkedBackingArray() {
+ shrinkToLength();
+ return mValues;
+ }
+
+ /**
+ * Get the String representation of an item in the array, for use by {@link #toString()}.
+ */
+ protected String valueToString(int item) {
+ return String.valueOf(item);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ final StringJoiner joiner = new StringJoiner(",", "[", "]");
+ forEach(item -> joiner.add(valueToString(item)));
+ return joiner.toString();
+ }
+}
diff --git a/framework/src/android/net/IRoutingCoordinator.aidl b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
similarity index 89%
rename from framework/src/android/net/IRoutingCoordinator.aidl
rename to staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
index cf02ec4..72a4a94 100644
--- a/framework/src/android/net/IRoutingCoordinator.aidl
+++ b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package android.net;
+package com.android.net.module.util;
import android.net.RouteInfo;
/** @hide */
+// TODO: b/350630377 - This @Descriptor annotation workaround is to prevent the DESCRIPTOR from
+// being jarjared which changes the DESCRIPTOR and casues "java.lang.SecurityException: Binder
+// invocation to an incorrect interface" when calling the IPC.
+@Descriptor("value=no.jarjar.com.android.net.module.util.IRoutingCoordinator")
interface IRoutingCoordinator {
/**
* Add a route for specific network
diff --git a/framework/src/android/net/RoutingCoordinatorManager.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
similarity index 95%
rename from framework/src/android/net/RoutingCoordinatorManager.java
rename to staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
index a9e7eef..02e3643 100644
--- a/framework/src/android/net/RoutingCoordinatorManager.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.net;
+package com.android.net.module.util;
import android.content.Context;
-import android.os.Build;
+import android.net.RouteInfo;
+import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
/**
* A manager class for talking to the routing coordinator service.
@@ -30,15 +30,14 @@
* by the build rules. Do not change build rules to gain access to this class from elsewhere.
* @hide
*/
-@RequiresApi(Build.VERSION_CODES.S)
public class RoutingCoordinatorManager {
@NonNull final Context mContext;
@NonNull final IRoutingCoordinator mService;
public RoutingCoordinatorManager(@NonNull final Context context,
- @NonNull final IRoutingCoordinator service) {
+ @NonNull final IBinder binder) {
mContext = context;
- mService = service;
+ mService = IRoutingCoordinator.Stub.asInterface(binder);
}
/**
diff --git a/service/src/com/android/server/connectivity/RoutingCoordinatorService.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
similarity index 98%
rename from service/src/com/android/server/connectivity/RoutingCoordinatorService.java
rename to staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
index 742a2cc..c75b860 100644
--- a/service/src/com/android/server/connectivity/RoutingCoordinatorService.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package com.android.net.module.util;
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
import android.annotation.NonNull;
import android.net.INetd;
-import android.net.IRoutingCoordinator;
+
import android.net.RouteInfo;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
diff --git a/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java b/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
index cd6bfec..a638cc4 100644
--- a/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
+++ b/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
@@ -19,6 +19,7 @@
import android.system.ErrnoException;
import android.util.Pair;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -60,6 +61,7 @@
public class SingleWriterBpfMap<K extends Struct, V extends Struct> extends BpfMap<K, V> {
// HashMap instead of ArrayMap because it performs better on larger maps, and many maps used in
// our code can contain hundreds of items.
+ @GuardedBy("this")
private final HashMap<K, V> mCache = new HashMap<>();
// This should only ever be called (hence private) once for a given 'path'.
@@ -72,10 +74,12 @@
super(path, BPF_F_RDWR_EXCLUSIVE, key, value);
// Populate cache with the current map contents.
- K currentKey = super.getFirstKey();
- while (currentKey != null) {
- mCache.put(currentKey, super.getValue(currentKey));
- currentKey = super.getNextKey(currentKey);
+ synchronized (this) {
+ K currentKey = super.getFirstKey();
+ while (currentKey != null) {
+ mCache.put(currentKey, super.getValue(currentKey));
+ currentKey = super.getNextKey(currentKey);
+ }
}
}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
index df7010e..0c49edc 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkLinkMessage.java
@@ -62,6 +62,17 @@
mInterfaceName = null;
}
+ @VisibleForTesting
+ public RtNetlinkLinkMessage(@NonNull StructNlMsgHdr nlmsghdr,
+ int mtu, @NonNull StructIfinfoMsg ifinfomsg, @NonNull MacAddress hardwareAddress,
+ @NonNull String interfaceName) {
+ super(nlmsghdr);
+ mMtu = mtu;
+ mIfinfomsg = ifinfomsg;
+ mHardwareAddress = hardwareAddress;
+ mInterfaceName = interfaceName;
+ }
+
public int getMtu() {
return mMtu;
}
diff --git a/staticlibs/framework/com/android/net/module/util/DnsUtils.java b/staticlibs/framework/com/android/net/module/util/DnsUtils.java
new file mode 100644
index 0000000..19ffd72
--- /dev/null
+++ b/staticlibs/framework/com/android/net/module/util/DnsUtils.java
@@ -0,0 +1,95 @@
+/*
+ * 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.net.module.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Dns common utility functions.
+ *
+ * @hide
+ */
+public class DnsUtils {
+
+ private DnsUtils() { }
+
+ /**
+ * Convert the string to DNS case-insensitive uppercase.
+ *
+ * Per rfc6762#page-46, accented characters are not defined to be automatically equivalent to
+ * their unaccented counterparts. So the "DNS uppercase" should be if character is a-z then they
+ * transform into A-Z. Otherwise, they are kept as-is.
+ */
+ public static String toDnsUpperCase(@NonNull String string) {
+ final char[] outChars = new char[string.length()];
+ for (int i = 0; i < string.length(); i++) {
+ outChars[i] = toDnsUpperCase(string.charAt(i));
+ }
+ return new String(outChars);
+ }
+
+ /**
+ * Convert the array of labels to DNS case-insensitive uppercase.
+ */
+ public static String[] toDnsLabelsUpperCase(@NonNull String[] labels) {
+ final String[] outStrings = new String[labels.length];
+ for (int i = 0; i < labels.length; ++i) {
+ outStrings[i] = toDnsUpperCase(labels[i]);
+ }
+ return outStrings;
+ }
+
+ /**
+ * Compare two strings by DNS case-insensitive uppercase.
+ */
+ public static boolean equalsIgnoreDnsCase(@Nullable String a, @Nullable String b) {
+ if (a == null || b == null) {
+ return a == null && b == null;
+ }
+ if (a.length() != b.length()) return false;
+ for (int i = 0; i < a.length(); i++) {
+ if (toDnsUpperCase(a.charAt(i)) != toDnsUpperCase(b.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare two set of DNS labels by DNS case-insensitive uppercase.
+ */
+ public static boolean equalsDnsLabelIgnoreDnsCase(@NonNull String[] a, @NonNull String[] b) {
+ if (a == b) {
+ return true;
+ }
+ int length = a.length;
+ if (b.length != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!equalsIgnoreDnsCase(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static char toDnsUpperCase(char a) {
+ return a >= 'a' && a <= 'z' ? (char) (a - ('a' - 'A')) : a;
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index a8c50d8..f1ff2e4 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -260,6 +260,18 @@
public static final short DNS_OVER_TLS_PORT = 853;
/**
+ * Dns query type constants.
+ *
+ * See also:
+ * - https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.2
+ */
+ public static final int TYPE_A = 1;
+ public static final int TYPE_PTR = 12;
+ public static final int TYPE_TXT = 16;
+ public static final int TYPE_AAAA = 28;
+ public static final int TYPE_SRV = 33;
+
+ /**
* IEEE802.11 standard constants.
*
* See also:
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
index cd51004..4bcd259 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <linux/bpf.h>
#include <poll.h>
+#include <sys/epoll.h>
#include <sys/mman.h>
#include <utils/Log.h>
@@ -33,7 +34,7 @@
// BpfRingbufBase contains the non-templated functionality of BPF ring buffers.
class BpfRingbufBase {
public:
- ~BpfRingbufBase() {
+ virtual ~BpfRingbufBase() {
if (mConsumerPos) munmap(mConsumerPos, mConsumerSize);
if (mProducerPos) munmap(mProducerPos, mProducerSize);
mConsumerPos = nullptr;
@@ -139,12 +140,24 @@
static base::Result<std::unique_ptr<BpfRingbuf<Value>>> Create(
const char* path);
+ int epoll_ctl_add(int epfd, struct epoll_event *event) {
+ return epoll_ctl(epfd, EPOLL_CTL_ADD, mRingFd.get(), event);
+ }
+
+ int epoll_ctl_mod(int epfd, struct epoll_event *event) {
+ return epoll_ctl(epfd, EPOLL_CTL_MOD, mRingFd.get(), event);
+ }
+
+ int epoll_ctl_del(int epfd) {
+ return epoll_ctl(epfd, EPOLL_CTL_DEL, mRingFd.get(), NULL);
+ }
+
// Consumes all messages from the ring buffer, passing them to the callback.
// Returns the number of messages consumed or a non-ok result on error. If the
// ring buffer has no pending messages an OK result with count 0 is returned.
base::Result<int> ConsumeAll(const MessageCallback& callback);
- private:
+ protected:
// Empty ctor for use by Create.
BpfRingbuf() : BpfRingbufBase(sizeof(Value)) {}
};
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Log.h b/staticlibs/netd/libnetdutils/include/netdutils/Log.h
index d266cbc..2de5ed7 100644
--- a/staticlibs/netd/libnetdutils/include/netdutils/Log.h
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Log.h
@@ -203,7 +203,7 @@
void record(Level lvl, const std::string& entry);
mutable std::shared_mutex mLock;
- std::deque<const std::string> mEntries; // GUARDED_BY(mLock), when supported
+ std::deque<std::string> mEntries; // GUARDED_BY(mLock), when supported
};
} // namespace netdutils
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index cf67a82..91f94b5 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -27,6 +27,7 @@
"net-utils-device-common-ip",
"net-utils-device-common-struct-base",
"net-utils-device-common-wear",
+ "net-utils-service-connectivity",
],
libs: [
"android.test.runner",
@@ -61,3 +62,29 @@
strict_updatability_linting: true,
},
}
+
+python_test_host {
+ name: "NetworkStaticLibHostPythonTests",
+ srcs: [
+ "host/python/*.py",
+ ],
+ main: "host/python/run_tests.py",
+ libs: [
+ "mobly",
+ "net-tests-utils-host-python-common",
+ ],
+ test_config: "host/python/test_config.xml",
+ test_suites: [
+ "general-tests",
+ ],
+ // MoblyBinaryHostTest doesn't support unit_test.
+ test_options: {
+ unit_test: false,
+ },
+ // Needed for applying VirtualEnv.
+ version: {
+ py3: {
+ embedded_launcher: false,
+ },
+ },
+}
diff --git a/staticlibs/tests/unit/host/python/adb_utils_test.py b/staticlibs/tests/unit/host/python/adb_utils_test.py
new file mode 100644
index 0000000..8fcca37
--- /dev/null
+++ b/staticlibs/tests/unit/host/python/adb_utils_test.py
@@ -0,0 +1,122 @@
+# 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.
+
+from unittest.mock import MagicMock, patch
+from absl.testing import parameterized
+from mobly import asserts
+from mobly import base_test
+from mobly import config_parser
+from net_tests_utils.host.python import adb_utils
+from net_tests_utils.host.python.assert_utils import UnexpectedBehaviorError
+
+
+class TestAdbUtils(base_test.BaseTestClass, parameterized.TestCase):
+
+ def __init__(self, configs: config_parser.TestRunConfig):
+ super().__init__(configs)
+
+ def setup_test(self):
+ self.mock_ad = MagicMock() # Mock Android device object
+ self.mock_ad.log = MagicMock()
+ self.mock_ad.adb.shell.return_value = b"" # Default empty return for shell
+
+ @patch(
+ "net_tests_utils.host.python.adb_utils.expect_dumpsys_state_with_retry"
+ )
+ @patch("net_tests_utils.host.python.adb_utils._set_screen_state")
+ def test_set_doze_mode_enable(
+ self, mock_set_screen_state, mock_expect_dumpsys_state
+ ):
+ adb_utils.set_doze_mode(self.mock_ad, True)
+ mock_set_screen_state.assert_called_once_with(self.mock_ad, False)
+
+ @patch(
+ "net_tests_utils.host.python.adb_utils.expect_dumpsys_state_with_retry"
+ )
+ def test_set_doze_mode_disable(self, mock_expect_dumpsys_state):
+ adb_utils.set_doze_mode(self.mock_ad, False)
+
+ @patch("net_tests_utils.host.python.adb_utils._get_screen_state")
+ def test_set_screen_state_success(self, mock_get_screen_state):
+ mock_get_screen_state.side_effect = [False, True] # Simulate toggle
+ adb_utils._set_screen_state(self.mock_ad, True)
+
+ @patch("net_tests_utils.host.python.adb_utils._get_screen_state")
+ def test_set_screen_state_failure(self, mock_get_screen_state):
+ mock_get_screen_state.return_value = False # State doesn't change
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ adb_utils._set_screen_state(self.mock_ad, True)
+
+ @parameterized.parameters(
+ ("Awake", True),
+ ("Asleep", False),
+ ("Dozing", False),
+ ("SomeOtherState", False),
+ ) # Declare inputs for state_str and expected_result.
+ @patch("net_tests_utils.host.python.adb_utils.get_value_of_key_from_dumpsys")
+ def test_get_screen_state(self, state_str, expected_result, mock_get_value):
+ mock_get_value.return_value = state_str
+ asserts.assert_equal(
+ adb_utils._get_screen_state(self.mock_ad), expected_result
+ )
+
+ def test_get_value_of_key_from_dumpsys(self):
+ self.mock_ad.adb.shell.return_value = (
+ b"mWakefulness=Awake\nmOtherKey=SomeValue"
+ )
+ result = adb_utils.get_value_of_key_from_dumpsys(
+ self.mock_ad, "power", "mWakefulness"
+ )
+ asserts.assert_equal(result, "Awake")
+
+ @parameterized.parameters(
+ (True, ["true"]),
+ (False, ["false"]),
+ (
+ True,
+ ["false", "true"],
+ ), # Expect True, get False which is unexpected, then get True
+ (
+ False,
+ ["true", "false"],
+ ), # Expect False, get True which is unexpected, then get False
+ ) # Declare inputs for expected_state and returned_value
+ @patch("net_tests_utils.host.python.adb_utils.get_value_of_key_from_dumpsys")
+ def test_expect_dumpsys_state_with_retry_success(
+ self, expected_state, returned_value, mock_get_value
+ ):
+ mock_get_value.side_effect = returned_value
+ # Verify the method returns and does not throw.
+ adb_utils.expect_dumpsys_state_with_retry(
+ self.mock_ad, "service", "key", expected_state, 0
+ )
+
+ @patch("net_tests_utils.host.python.adb_utils.get_value_of_key_from_dumpsys")
+ def test_expect_dumpsys_state_with_retry_failure(self, mock_get_value):
+ mock_get_value.return_value = "false"
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ adb_utils.expect_dumpsys_state_with_retry(
+ self.mock_ad, "service", "key", True, 0
+ )
+
+ @patch("net_tests_utils.host.python.adb_utils.get_value_of_key_from_dumpsys")
+ def test_expect_dumpsys_state_with_retry_not_found(self, mock_get_value):
+ # Simulate the get_value_of_key_from_dumpsys cannot find the give key.
+ mock_get_value.return_value = None
+
+ # Expect the function to raise UnexpectedBehaviorError due to the exception
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ adb_utils.expect_dumpsys_state_with_retry(
+ self.mock_ad, "service", "key", True
+ )
diff --git a/staticlibs/tests/unit/host/python/apf_utils_test.py b/staticlibs/tests/unit/host/python/apf_utils_test.py
new file mode 100644
index 0000000..caaf959
--- /dev/null
+++ b/staticlibs/tests/unit/host/python/apf_utils_test.py
@@ -0,0 +1,175 @@
+# 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.
+
+from unittest.mock import MagicMock, patch
+from absl.testing import parameterized
+from mobly import asserts
+from mobly import base_test
+from mobly import config_parser
+from mobly.controllers.android_device_lib.adb import AdbError
+from net_tests_utils.host.python.apf_utils import (
+ ApfCapabilities,
+ PatternNotFoundException,
+ UnsupportedOperationException,
+ get_apf_capabilities,
+ get_apf_counter,
+ get_apf_counters_from_dumpsys,
+ get_hardware_address,
+ send_broadcast_empty_ethercat_packet,
+ send_raw_packet_downstream,
+)
+from net_tests_utils.host.python.assert_utils import UnexpectedBehaviorError
+
+
+class TestApfUtils(base_test.BaseTestClass, parameterized.TestCase):
+
+ def __init__(self, configs: config_parser.TestRunConfig):
+ super().__init__(configs)
+
+ def setup_test(self):
+ self.mock_ad = MagicMock() # Mock Android device object
+
+ @patch("net_tests_utils.host.python.adb_utils.get_dumpsys_for_service")
+ def test_get_apf_counters_from_dumpsys_success(
+ self, mock_get_dumpsys: MagicMock
+ ) -> None:
+ mock_get_dumpsys.return_value = """
+IpClient.wlan0
+ APF packet counters:
+ COUNTER_NAME1: 123
+ COUNTER_NAME2: 456
+"""
+ counters = get_apf_counters_from_dumpsys(self.mock_ad, "wlan0")
+ asserts.assert_equal(counters, {"COUNTER_NAME1": 123, "COUNTER_NAME2": 456})
+
+ @patch("net_tests_utils.host.python.adb_utils.get_dumpsys_for_service")
+ def test_get_apf_counters_from_dumpsys_exceptions(
+ self, mock_get_dumpsys: MagicMock
+ ) -> None:
+ test_cases = [
+ "",
+ "IpClient.wlan0\n",
+ "IpClient.wlan0\n APF packet counters:\n",
+ """
+IpClient.wlan1
+ APF packet counters:
+ COUNTER_NAME1: 123
+ COUNTER_NAME2: 456
+""",
+ ]
+
+ for dumpsys_output in test_cases:
+ mock_get_dumpsys.return_value = dumpsys_output
+ with asserts.assert_raises(PatternNotFoundException):
+ get_apf_counters_from_dumpsys(self.mock_ad, "wlan0")
+
+ @patch("net_tests_utils.host.python.apf_utils.get_apf_counters_from_dumpsys")
+ def test_get_apf_counter(self, mock_get_counters: MagicMock) -> None:
+ iface = "wlan0"
+ mock_get_counters.return_value = {
+ "COUNTER_NAME1": 123,
+ "COUNTER_NAME2": 456,
+ }
+ asserts.assert_equal(
+ get_apf_counter(self.mock_ad, iface, "COUNTER_NAME1"), 123
+ )
+ # Not found
+ asserts.assert_equal(
+ get_apf_counter(self.mock_ad, iface, "COUNTER_NAME3"), 0
+ )
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_get_hardware_address_success(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = """
+46: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq ...
+ link/ether 72:05:77:82:21:e0 brd ff:ff:ff:ff:ff:ff
+"""
+ mac_address = get_hardware_address(self.mock_ad, "wlan0")
+ asserts.assert_equal(mac_address, "72:05:77:82:21:E0")
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_get_hardware_address_not_found(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = "Some output without MAC address"
+ with asserts.assert_raises(PatternNotFoundException):
+ get_hardware_address(self.mock_ad, "wlan0")
+
+ @patch("net_tests_utils.host.python.apf_utils.get_hardware_address")
+ @patch("net_tests_utils.host.python.apf_utils.send_raw_packet_downstream")
+ def test_send_broadcast_empty_ethercat_packet(
+ self,
+ mock_send_raw_packet_downstream: MagicMock,
+ mock_get_hardware_address: MagicMock,
+ ) -> None:
+ mock_get_hardware_address.return_value = "12:34:56:78:90:AB"
+ send_broadcast_empty_ethercat_packet(self.mock_ad, "eth0")
+ # Assuming you'll mock the packet construction part, verify calls to send_raw_packet_downstream.
+ mock_send_raw_packet_downstream.assert_called_once()
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_send_raw_packet_downstream_success(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = "" # Successful command output
+ iface_name = "eth0"
+ packet_in_hex = "AABBCCDDEEFF"
+ send_raw_packet_downstream(self.mock_ad, iface_name, packet_in_hex)
+ mock_adb_shell.assert_called_once_with(
+ self.mock_ad,
+ "cmd network_stack send-raw-packet-downstream"
+ f" {iface_name} {packet_in_hex}",
+ )
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_send_raw_packet_downstream_failure(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = ( # Unexpected command output
+ "Any Unexpected Output"
+ )
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ send_raw_packet_downstream(self.mock_ad, "eth0", "AABBCCDDEEFF")
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_send_raw_packet_downstream_unsupported(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.side_effect = AdbError(
+ cmd="", stdout="Unknown command", stderr="", ret_code=3
+ )
+ with asserts.assert_raises(UnsupportedOperationException):
+ send_raw_packet_downstream(self.mock_ad, "eth0", "AABBCCDDEEFF")
+
+ @parameterized.parameters(
+ ("2,2048,1", ApfCapabilities(2, 2048, 1)), # Valid input
+ ("3,1024,0", ApfCapabilities(3, 1024, 0)), # Valid input
+ ("invalid,output", ApfCapabilities(0, 0, 0)), # Invalid input
+ ("", ApfCapabilities(0, 0, 0)), # Empty input
+ )
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_get_apf_capabilities(
+ self, mock_output, expected_result, mock_adb_shell
+ ):
+ """Tests the get_apf_capabilities function with various inputs and expected results."""
+ # Configure the mock adb_shell to return the specified output
+ mock_adb_shell.return_value = mock_output
+
+ # Call the function under test
+ result = get_apf_capabilities(self.mock_ad, "wlan0")
+
+ # Assert that the result matches the expected result
+ asserts.assert_equal(result, expected_result)
diff --git a/staticlibs/tests/unit/host/python/assert_utils_test.py b/staticlibs/tests/unit/host/python/assert_utils_test.py
new file mode 100644
index 0000000..7a33373
--- /dev/null
+++ b/staticlibs/tests/unit/host/python/assert_utils_test.py
@@ -0,0 +1,94 @@
+# 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.
+
+from mobly import asserts
+from mobly import base_test
+from net_tests_utils.host.python.assert_utils import UnexpectedBehaviorError, expect_with_retry
+
+
+class TestAssertUtils(base_test.BaseTestClass):
+
+ def test_predicate_succeed(self):
+ """Test when the predicate becomes True within retries."""
+ call_count = 0
+
+ def predicate():
+ nonlocal call_count
+ call_count += 1
+ return call_count > 2 # True on the third call
+
+ expect_with_retry(predicate, max_retries=5, retry_interval_sec=0)
+ asserts.assert_equal(call_count, 3) # Ensure it was called exactly 3 times
+
+ def test_predicate_failed(self):
+ """Test when the predicate never becomes True."""
+
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ expect_with_retry(
+ predicate=lambda: False, max_retries=3, retry_interval_sec=0
+ )
+
+ def test_retry_action_not_called_succeed(self):
+ """Test that the retry_action is not called if the predicate returns true in the first try."""
+ retry_action_called = False
+
+ def retry_action():
+ nonlocal retry_action_called
+ retry_action_called = True
+
+ expect_with_retry(
+ predicate=lambda: True,
+ retry_action=retry_action,
+ max_retries=5,
+ retry_interval_sec=0,
+ )
+ asserts.assert_false(
+ retry_action_called, "retry_action called."
+ ) # Assert retry_action was NOT called
+
+ def test_retry_action_not_called_failed(self):
+ """Test that the retry_action is not called if the max_retries is reached."""
+ retry_action_called = False
+
+ def retry_action():
+ nonlocal retry_action_called
+ retry_action_called = True
+
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ expect_with_retry(
+ predicate=lambda: False,
+ retry_action=retry_action,
+ max_retries=1,
+ retry_interval_sec=0,
+ )
+ asserts.assert_false(
+ retry_action_called, "retry_action called."
+ ) # Assert retry_action was NOT called
+
+ def test_retry_action_called(self):
+ """Test that the retry_action is executed when provided."""
+ retry_action_called = False
+
+ def retry_action():
+ nonlocal retry_action_called
+ retry_action_called = True
+
+ with asserts.assert_raises(UnexpectedBehaviorError):
+ expect_with_retry(
+ predicate=lambda: False,
+ retry_action=retry_action,
+ max_retries=2,
+ retry_interval_sec=0,
+ )
+ asserts.assert_true(retry_action_called, "retry_action not called.")
diff --git a/staticlibs/tests/unit/host/python/run_tests.py b/staticlibs/tests/unit/host/python/run_tests.py
new file mode 100644
index 0000000..fa6a310
--- /dev/null
+++ b/staticlibs/tests/unit/host/python/run_tests.py
@@ -0,0 +1,35 @@
+# 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.
+
+"""Main entrypoint for all of unittest."""
+
+import sys
+from host.python.adb_utils_test import TestAdbUtils
+from host.python.apf_utils_test import TestApfUtils
+from host.python.assert_utils_test import TestAssertUtils
+from mobly import suite_runner
+
+
+if __name__ == "__main__":
+ # For MoblyBinaryHostTest, this entry point will be called twice:
+ # 1. List tests.
+ # <mobly-par-file-name> -- --list_tests
+ # 2. Run tests.
+ # <mobly-par-file-name> -- --config=<yaml-path> --device_serial=<device-serial> --log_path=<log-path>
+ # Strip the "--" since suite runner doesn't recognize it.
+ sys.argv.pop(1)
+ # TODO: make the tests can be executed without manually list classes.
+ suite_runner.run_suite(
+ [TestAssertUtils, TestAdbUtils, TestApfUtils], sys.argv
+ )
diff --git a/staticlibs/tests/unit/host/python/test_config.xml b/staticlibs/tests/unit/host/python/test_config.xml
new file mode 100644
index 0000000..d3b200a
--- /dev/null
+++ b/staticlibs/tests/unit/host/python/test_config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for NetworkStaticLibHostPythonTests">
+ <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
+ <option name="dep-module" value="absl-py" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest" >
+ <option name="mobly-par-file-name" value="NetworkStaticLibHostPythonTests" />
+ <option name="mobly-test-timeout" value="3m" />
+ </test>
+</configuration>
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/DnsUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/DnsUtilsTest.kt
new file mode 100644
index 0000000..7b1f08a
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/DnsUtilsTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.net.module.util
+
+import com.android.net.module.util.DnsUtils.equalsDnsLabelIgnoreDnsCase
+import com.android.net.module.util.DnsUtils.equalsIgnoreDnsCase
+import com.android.net.module.util.DnsUtils.toDnsLabelsUpperCase
+import com.android.net.module.util.DnsUtils.toDnsUpperCase
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+class DnsUtilsTest {
+ @Test
+ fun testToDnsUpperCase() {
+ assertEquals("TEST", toDnsUpperCase("TEST"))
+ assertEquals("TEST", toDnsUpperCase("TeSt"))
+ assertEquals("TEST", toDnsUpperCase("test"))
+ assertEquals("TÉST", toDnsUpperCase("TÉST"))
+ assertEquals("ţéST", toDnsUpperCase("ţést"))
+ // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
+ // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
+ assertEquals(
+ "TEST: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
+ toDnsUpperCase("Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ")
+ )
+ // Also test some characters where the first surrogate is not \ud800
+ assertEquals(
+ "TEST: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
+ toDnsUpperCase(
+ "Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"
+ )
+ )
+ }
+
+ @Test
+ fun testToDnsLabelsUpperCase() {
+ assertArrayEquals(
+ arrayOf("TEST", "TÉST", "ţéST"),
+ toDnsLabelsUpperCase(arrayOf("TeSt", "TÉST", "ţést"))
+ )
+ }
+
+ @Test
+ fun testEqualsIgnoreDnsCase() {
+ assertTrue(equalsIgnoreDnsCase("TEST", "Test"))
+ assertTrue(equalsIgnoreDnsCase("TEST", "test"))
+ assertTrue(equalsIgnoreDnsCase("test", "TeSt"))
+ assertTrue(equalsIgnoreDnsCase("Tést", "tést"))
+ assertFalse(equalsIgnoreDnsCase("ŢÉST", "ţést"))
+ // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
+ // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
+ assertTrue(equalsIgnoreDnsCase(
+ "test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
+ "Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- "
+ ))
+ // Also test some characters where the first surrogate is not \ud800
+ assertTrue(equalsIgnoreDnsCase(
+ "test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
+ "Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"
+ ))
+ }
+
+ @Test
+ fun testEqualsLabelIgnoreDnsCase() {
+ assertTrue(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "tést")))
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/GrowingIntArrayTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/GrowingIntArrayTest.kt
new file mode 100644
index 0000000..bdcb8c0
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/GrowingIntArrayTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.net.module.util
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import kotlin.test.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GrowingIntArrayTest {
+ @Test
+ fun testAddAndGet() {
+ val array = GrowingIntArray(1)
+ array.add(-1)
+ array.add(0)
+ array.add(2)
+
+ assertEquals(-1, array.get(0))
+ assertEquals(0, array.get(1))
+ assertEquals(2, array.get(2))
+ assertEquals(3, array.length())
+ }
+
+ @Test
+ fun testForEach() {
+ val array = GrowingIntArray(10)
+ array.add(-1)
+ array.add(0)
+ array.add(2)
+
+ val actual = mutableListOf<Int>()
+ array.forEach { actual.add(it) }
+
+ val expected = listOf(-1, 0, 2)
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun testForEach_EmptyArray() {
+ val array = GrowingIntArray(10)
+ array.forEach {
+ fail("This should not be called")
+ }
+ }
+
+ @Test
+ fun testRemoveValues() {
+ val array = GrowingIntArray(10)
+ array.add(-1)
+ array.add(0)
+ array.add(2)
+
+ array.removeValues { it <= 0 }
+ assertEquals(1, array.length())
+ assertEquals(2, array.get(0))
+ }
+
+ @Test
+ fun testContains() {
+ val array = GrowingIntArray(10)
+ array.add(-1)
+ array.add(2)
+
+ assertTrue(array.contains(-1))
+ assertTrue(array.contains(2))
+
+ assertFalse(array.contains(0))
+ assertFalse(array.contains(3))
+ }
+
+ @Test
+ fun testClear() {
+ val array = GrowingIntArray(10)
+ array.add(-1)
+ array.add(2)
+ array.clear()
+
+ assertEquals(0, array.length())
+ }
+
+ @Test
+ fun testEnsureHasCapacity() {
+ val array = GrowingIntArray(0)
+ array.add(42)
+ array.ensureHasCapacity(2)
+
+ assertEquals(3, array.backingArrayLength)
+ }
+
+ @Test
+ fun testGetShrinkedBackingArray() {
+ val array = GrowingIntArray(10)
+ array.add(-1)
+ array.add(2)
+
+ assertContentEquals(intArrayOf(-1, 2), array.shrinkedBackingArray)
+ }
+
+ @Test
+ fun testToString() {
+ assertEquals("[]", GrowingIntArray(10).toString())
+ assertEquals("[1,2,3]", GrowingIntArray(3).apply {
+ add(1)
+ add(2)
+ add(3)
+ }.toString())
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/RoutingCoordinatorServiceTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt
similarity index 97%
rename from tests/unit/java/com/android/server/connectivity/RoutingCoordinatorServiceTest.kt
rename to staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt
index 4e15d5f..b04561c 100644
--- a/tests/unit/java/com/android/server/connectivity/RoutingCoordinatorServiceTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity
+package com.android.net.module.util
import android.net.INetd
import android.os.Build
diff --git a/staticlibs/tests/unit/src/com/android/testutils/DefaultNetworkRestoreMonitorTest.kt b/staticlibs/tests/unit/src/com/android/testutils/DefaultNetworkRestoreMonitorTest.kt
new file mode 100644
index 0000000..7e508fb
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/testutils/DefaultNetworkRestoreMonitorTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.content.Context
+import android.content.pm.PackageManager
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import org.junit.Test
+import org.junit.runner.Description
+import org.junit.runner.notification.RunListener
+import org.junit.runner.notification.RunNotifier
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+class DefaultNetworkRestoreMonitorTest {
+ private val restoreDefaultNetworkDesc =
+ Description.createSuiteDescription("RestoreDefaultNetwork")
+ private val testDesc = Description.createTestDescription("testClass", "testMethod")
+ private val wifiCap = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ .build()
+ private val cellCap = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ .build()
+ private val cm = mock(ConnectivityManager::class.java)
+ private val pm = mock(PackageManager::class.java).also {
+ doReturn(true).`when`(it).hasSystemFeature(anyString())
+ }
+ private val ctx = mock(Context::class.java).also {
+ doReturn(cm).`when`(it).getSystemService(ConnectivityManager::class.java)
+ doReturn(pm).`when`(it).getPackageManager()
+ }
+ private val notifier = mock(RunNotifier::class.java)
+ private val defaultNetworkMonitor = DefaultNetworkRestoreMonitor(
+ ctx,
+ notifier,
+ timeoutMs = 0
+ )
+
+ private fun getRunListener(): RunListener {
+ val captor = ArgumentCaptor.forClass(RunListener::class.java)
+ verify(notifier).addListener(captor.capture())
+ return captor.value
+ }
+
+ private fun mockDefaultNetworkCapabilities(cap: NetworkCapabilities?) {
+ if (cap == null) {
+ doNothing().`when`(cm).registerDefaultNetworkCallback(any())
+ return
+ }
+ doAnswer {
+ val callback = it.getArgument(0) as NetworkCallback
+ callback.onCapabilitiesChanged(Network(100), cap)
+ }.`when`(cm).registerDefaultNetworkCallback(any())
+ }
+
+ @Test
+ fun testDefaultNetworkRestoreMonitor_defaultNetworkRestored() {
+ mockDefaultNetworkCapabilities(wifiCap)
+ defaultNetworkMonitor.init(mock(ConnectUtil::class.java))
+
+ val listener = getRunListener()
+ listener.testFinished(testDesc)
+
+ defaultNetworkMonitor.reportResultAndCleanUp(restoreDefaultNetworkDesc)
+ val inOrder = inOrder(notifier)
+ inOrder.verify(notifier).fireTestStarted(restoreDefaultNetworkDesc)
+ inOrder.verify(notifier, never()).fireTestFailure(any())
+ inOrder.verify(notifier).fireTestFinished(restoreDefaultNetworkDesc)
+ inOrder.verify(notifier).removeListener(listener)
+ }
+
+ @Test
+ fun testDefaultNetworkRestoreMonitor_testStartWithoutDefaultNetwork() {
+ // There is no default network when the tests start
+ mockDefaultNetworkCapabilities(null)
+ defaultNetworkMonitor.init(mock(ConnectUtil::class.java))
+
+ mockDefaultNetworkCapabilities(wifiCap)
+ val listener = getRunListener()
+ listener.testFinished(testDesc)
+
+ defaultNetworkMonitor.reportResultAndCleanUp(restoreDefaultNetworkDesc)
+ val inOrder = inOrder(notifier)
+ inOrder.verify(notifier).fireTestStarted(restoreDefaultNetworkDesc)
+ // fireTestFailure is called
+ inOrder.verify(notifier).fireTestFailure(any())
+ inOrder.verify(notifier).fireTestFinished(restoreDefaultNetworkDesc)
+ inOrder.verify(notifier).removeListener(listener)
+ }
+
+ @Test
+ fun testDefaultNetworkRestoreMonitor_testEndWithoutDefaultNetwork() {
+ mockDefaultNetworkCapabilities(wifiCap)
+ defaultNetworkMonitor.init(mock(ConnectUtil::class.java))
+
+ // There is no default network after the test
+ mockDefaultNetworkCapabilities(null)
+ val listener = getRunListener()
+ listener.testFinished(testDesc)
+
+ defaultNetworkMonitor.reportResultAndCleanUp(restoreDefaultNetworkDesc)
+ val inOrder = inOrder(notifier)
+ inOrder.verify(notifier).fireTestStarted(restoreDefaultNetworkDesc)
+ // fireTestFailure is called with method name
+ inOrder.verify(
+ notifier
+ ).fireTestFailure(
+ argThat{failure -> failure.exception.message?.contains("testMethod") ?: false}
+ )
+ inOrder.verify(notifier).fireTestFinished(restoreDefaultNetworkDesc)
+ inOrder.verify(notifier).removeListener(listener)
+ }
+
+ @Test
+ fun testDefaultNetworkRestoreMonitor_testChangeDefaultNetwork() {
+ mockDefaultNetworkCapabilities(wifiCap)
+ defaultNetworkMonitor.init(mock(ConnectUtil::class.java))
+
+ // The default network transport types change after the test
+ mockDefaultNetworkCapabilities(cellCap)
+ val listener = getRunListener()
+ listener.testFinished(testDesc)
+
+ defaultNetworkMonitor.reportResultAndCleanUp(restoreDefaultNetworkDesc)
+ val inOrder = inOrder(notifier)
+ inOrder.verify(notifier).fireTestStarted(restoreDefaultNetworkDesc)
+ // fireTestFailure is called with method name
+ inOrder.verify(
+ notifier
+ ).fireTestFailure(
+ argThat{failure -> failure.exception.message?.contains("testMethod") ?: false}
+ )
+ inOrder.verify(notifier).fireTestFinished(restoreDefaultNetworkDesc)
+ inOrder.verify(notifier).removeListener(listener)
+ }
+}
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index 3843b90..4749e75 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -42,6 +42,7 @@
"net-utils-device-common-struct",
"net-utils-device-common-struct-base",
"net-utils-device-common-wear",
+ "net-utils-framework-connectivity",
"modules-utils-build_system",
],
lint: {
@@ -86,8 +87,8 @@
java_test_host {
name: "net-tests-utils-host-common",
srcs: [
- "host/**/*.java",
- "host/**/*.kt",
+ "host/java/**/*.java",
+ "host/java/**/*.kt",
],
libs: ["tradefed"],
test_suites: [
@@ -104,3 +105,11 @@
],
data: [":ConnectivityTestPreparer"],
}
+
+python_library_host {
+ name: "net-tests-utils-host-python-common",
+ srcs: [
+ "host/python/*.py",
+ ],
+ pkg_path: "net_tests_utils",
+}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DefaultNetworkRestoreMonitor.kt b/staticlibs/testutils/devicetests/com/android/testutils/DefaultNetworkRestoreMonitor.kt
new file mode 100644
index 0000000..4a594e6
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/DefaultNetworkRestoreMonitor.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.content.Context
+import android.content.pm.PackageManager
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import com.android.internal.annotations.VisibleForTesting
+import com.android.net.module.util.BitUtils
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import org.junit.runner.Description
+import org.junit.runner.notification.Failure
+import org.junit.runner.notification.RunListener
+import org.junit.runner.notification.RunNotifier
+
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+class DefaultNetworkRestoreMonitor(
+ ctx: Context,
+ private val notifier: RunNotifier,
+ private val timeoutMs: Long = 30_000
+) {
+ var firstFailure: Exception? = null
+ var initialTransports = 0L
+ val cm = ctx.getSystemService(ConnectivityManager::class.java)!!
+ val pm = ctx.packageManager
+ val listener = object : RunListener() {
+ override fun testFinished(desc: Description) {
+ // Only the first method that does not restore the default network should be blamed.
+ if (firstFailure != null) {
+ return
+ }
+ val cb = TestableNetworkCallback()
+ cm.registerDefaultNetworkCallback(cb)
+ try {
+ cb.eventuallyExpect<RecorderCallback.CallbackEntry.CapabilitiesChanged>(
+ timeoutMs = timeoutMs
+ ) {
+ BitUtils.packBits(it.caps.transportTypes) == initialTransports &&
+ it.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ }
+ } catch (e: AssertionError) {
+ firstFailure = IllegalStateException(desc.methodName + " does not restore the" +
+ "default network, initialTransports = $initialTransports", e)
+ } finally {
+ cm.unregisterNetworkCallback(cb)
+ }
+ }
+ }
+
+ fun init(connectUtil: ConnectUtil) {
+ // Ensure Wi-Fi and cellular connection before running test to avoid starting test
+ // with unexpected default network.
+ // ConnectivityTestTargetPreparer does the same thing, but it's possible that previous tests
+ // don't enable DefaultNetworkRestoreMonitor and the default network is not restored.
+ // This can be removed if all tests enable DefaultNetworkRestoreMonitor
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ connectUtil.ensureWifiValidated()
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ connectUtil.ensureCellularValidated()
+ }
+
+ val capFuture = CompletableFuture<NetworkCapabilities>()
+ val cb = object : ConnectivityManager.NetworkCallback() {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ cap: NetworkCapabilities
+ ) {
+ capFuture.complete(cap)
+ }
+ }
+ cm.registerDefaultNetworkCallback(cb)
+ try {
+ val cap = capFuture.get(100, TimeUnit.MILLISECONDS)
+ initialTransports = BitUtils.packBits(cap.transportTypes)
+ } catch (e: Exception) {
+ firstFailure = IllegalStateException(
+ "Failed to get default network status before starting tests", e
+ )
+ } finally {
+ cm.unregisterNetworkCallback(cb)
+ }
+ notifier.addListener(listener)
+ }
+
+ fun reportResultAndCleanUp(desc: Description) {
+ notifier.fireTestStarted(desc)
+ if (firstFailure != null) {
+ notifier.fireTestFailure(
+ Failure(desc, firstFailure)
+ )
+ }
+ notifier.fireTestFinished(desc)
+ notifier.removeListener(listener)
+ }
+}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
index 69fdbf8..a014834 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
@@ -16,6 +16,8 @@
package com.android.testutils
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
@@ -57,6 +59,10 @@
class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner(), Filterable, Sortable {
private val leakMonitorDesc = Description.createTestDescription(klass, "ThreadLeakMonitor")
private val shouldThreadLeakFailTest = klass.isAnnotationPresent(MonitorThreadLeak::class.java)
+ private val restoreDefaultNetworkDesc =
+ Description.createTestDescription(klass, "RestoreDefaultNetwork")
+ private val restoreDefaultNetwork = klass.isAnnotationPresent(RestoreDefaultNetwork::class.java)
+ val ctx = ApplicationProvider.getApplicationContext<Context>()
// Inference correctly infers Runner & Filterable & Sortable for |baseRunner|, but the
// Java bytecode doesn't have a way to express this. Give this type a name by wrapping it.
@@ -71,6 +77,10 @@
// TODO(b/307693729): Remove this annotation and monitor thread leak by default.
annotation class MonitorThreadLeak
+ // Annotation for test classes to indicate the test runner should verify the default network is
+ // restored after each test.
+ annotation class RestoreDefaultNetwork
+
private val baseRunner: RunnerWrapper<*>? = klass.let {
val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java)
val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java)
@@ -90,25 +100,10 @@
Modifier.isStatic(it.modifiers) &&
it.isAnnotationPresent(Parameterized.Parameters::class.java) }
- override fun run(notifier: RunNotifier) {
- if (baseRunner == null) {
- // Report a single, skipped placeholder test for this class, as the class is expected to
- // report results when run. In practice runners that apply the Filterable implementation
- // would see a NoTestsRemainException and not call the run method.
- notifier.fireTestIgnored(
- Description.createTestDescription(klass, "skippedClassForDevSdkMismatch"))
- return
- }
- if (!shouldThreadLeakFailTest) {
- baseRunner.run(notifier)
- return
- }
-
- // Dump threads as a baseline to monitor thread leaks.
- val threadCountsBeforeTest = getAllThreadNameCounts()
-
- baseRunner.run(notifier)
-
+ private fun checkThreadLeak(
+ notifier: RunNotifier,
+ threadCountsBeforeTest: Map<String, Int>
+ ) {
notifier.fireTestStarted(leakMonitorDesc)
val threadCountsAfterTest = getAllThreadNameCounts()
// TODO : move CompareOrUpdateResult to its own util instead of LinkProperties.
@@ -122,13 +117,48 @@
val increasedThreads = threadsDiff.updated
.filter { threadCountsBeforeTest[it.key]!! < it.value }
if (threadsDiff.added.isNotEmpty() || increasedThreads.isNotEmpty()) {
- notifier.fireTestFailure(Failure(leakMonitorDesc,
- IllegalStateException("Unexpected thread changes: $threadsDiff")))
+ notifier.fireTestFailure(Failure(
+ leakMonitorDesc,
+ IllegalStateException("Unexpected thread changes: $threadsDiff")
+ ))
}
+ notifier.fireTestFinished(leakMonitorDesc)
+ }
+
+ override fun run(notifier: RunNotifier) {
+ if (baseRunner == null) {
+ // Report a single, skipped placeholder test for this class, as the class is expected to
+ // report results when run. In practice runners that apply the Filterable implementation
+ // would see a NoTestsRemainException and not call the run method.
+ notifier.fireTestIgnored(
+ Description.createTestDescription(klass, "skippedClassForDevSdkMismatch")
+ )
+ return
+ }
+
+ val networkRestoreMonitor = if (restoreDefaultNetwork) {
+ DefaultNetworkRestoreMonitor(ctx, notifier).apply{
+ init(ConnectUtil(ctx))
+ }
+ } else {
+ null
+ }
+ val threadCountsBeforeTest = if (shouldThreadLeakFailTest) {
+ // Dump threads as a baseline to monitor thread leaks.
+ getAllThreadNameCounts()
+ } else {
+ null
+ }
+
+ baseRunner.run(notifier)
+
+ if (threadCountsBeforeTest != null) {
+ checkThreadLeak(notifier, threadCountsBeforeTest)
+ }
+ networkRestoreMonitor?.reportResultAndCleanUp(restoreDefaultNetworkDesc)
// Clears up internal state of all inline mocks.
// TODO: Call clearInlineMocks() at the end of each test.
Mockito.framework().clearInlineMocks()
- notifier.fireTestFinished(leakMonitorDesc)
}
private fun getAllThreadNameCounts(): Map<String, Int> {
@@ -152,6 +182,9 @@
if (shouldThreadLeakFailTest) {
it.addChild(leakMonitorDesc)
}
+ if (restoreDefaultNetwork) {
+ it.addChild(restoreDefaultNetworkDesc)
+ }
}
}
@@ -162,7 +195,14 @@
// When ignoring the tests, a skipped placeholder test is reported, so test count is 1.
if (baseRunner == null) return 1
- return baseRunner.testCount() + if (shouldThreadLeakFailTest) 1 else 0
+ var testCount = baseRunner.testCount()
+ if (shouldThreadLeakFailTest) {
+ testCount += 1
+ }
+ if (restoreDefaultNetwork) {
+ testCount += 1
+ }
+ return testCount
}
@Throws(NoTestsRemainException::class)
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
index 66362d4..ae43c15 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
@@ -44,13 +44,18 @@
object ANY_NETWORK : Network(-2)
fun anyNetwork() = ANY_NETWORK
-open class RecorderCallback private constructor(
- private val backingRecord: ArrayTrackRecord<CallbackEntry>
-) : NetworkCallback() {
- public constructor() : this(ArrayTrackRecord())
- protected constructor(src: RecorderCallback?) : this(src?.backingRecord ?: ArrayTrackRecord())
+private val DEFAULT_TAG = RecorderCallback::class.simpleName
+ ?: fail("Could not determine class name")
- private val TAG = this::class.simpleName
+open class RecorderCallback private constructor(
+ private val backingRecord: ArrayTrackRecord<CallbackEntry>,
+ val logTag: String
+) : NetworkCallback() {
+ public constructor(logTag: String = DEFAULT_TAG) : this(ArrayTrackRecord(), logTag)
+ protected constructor(src: RecorderCallback?, logTag: String) : this(
+ src?.backingRecord ?: ArrayTrackRecord(),
+ logTag
+ )
sealed class CallbackEntry {
// To get equals(), hashcode(), componentN() etc for free, the child classes of
@@ -123,7 +128,7 @@
val mark get() = history.mark
override fun onAvailable(network: Network) {
- Log.d(TAG, "onAvailable $network")
+ Log.d(logTag, "onAvailable $network")
history.add(Available(network))
}
@@ -131,22 +136,22 @@
// expect the callbacks not to record this, do not listen to PreCheck here.
override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
- Log.d(TAG, "onCapabilitiesChanged $network $caps")
+ Log.d(logTag, "onCapabilitiesChanged $network $caps")
history.add(CapabilitiesChanged(network, caps))
}
override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
- Log.d(TAG, "onLinkPropertiesChanged $network $lp")
+ Log.d(logTag, "onLinkPropertiesChanged $network $lp")
history.add(LinkPropertiesChanged(network, lp))
}
override fun onLocalNetworkInfoChanged(network: Network, info: LocalNetworkInfo) {
- Log.d(TAG, "onLocalNetworkInfoChanged $network $info")
+ Log.d(logTag, "onLocalNetworkInfoChanged $network $info")
history.add(LocalInfoChanged(network, info))
}
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
- Log.d(TAG, "onBlockedStatusChanged $network $blocked")
+ Log.d(logTag, "onBlockedStatusChanged $network $blocked")
history.add(BlockedStatus(network, blocked))
}
@@ -154,27 +159,27 @@
// fun onBlockedStatusChanged(network: Network, blocked: Int) {
// because on S, that needs to be "override fun", and on R, that cannot be "override fun".
override fun onNetworkSuspended(network: Network) {
- Log.d(TAG, "onNetworkSuspended $network $network")
+ Log.d(logTag, "onNetworkSuspended $network $network")
history.add(Suspended(network))
}
override fun onNetworkResumed(network: Network) {
- Log.d(TAG, "$network onNetworkResumed $network")
+ Log.d(logTag, "$network onNetworkResumed $network")
history.add(Resumed(network))
}
override fun onLosing(network: Network, maxMsToLive: Int) {
- Log.d(TAG, "onLosing $network $maxMsToLive")
+ Log.d(logTag, "onLosing $network $maxMsToLive")
history.add(Losing(network, maxMsToLive))
}
override fun onLost(network: Network) {
- Log.d(TAG, "onLost $network")
+ Log.d(logTag, "onLost $network")
history.add(Lost(network))
}
override fun onUnavailable() {
- Log.d(TAG, "onUnavailable")
+ Log.d(logTag, "onUnavailable")
history.add(Unavailable())
}
}
@@ -188,10 +193,11 @@
*/
open class TestableNetworkCallback private constructor(
src: TestableNetworkCallback?,
- val defaultTimeoutMs: Long = DEFAULT_TIMEOUT,
- val defaultNoCallbackTimeoutMs: Long = DEFAULT_NO_CALLBACK_TIMEOUT,
- val waiterFunc: Runnable = NOOP // "() -> Unit" would forbid calling with a void func from Java
-) : RecorderCallback(src) {
+ val defaultTimeoutMs: Long,
+ val defaultNoCallbackTimeoutMs: Long,
+ val waiterFunc: Runnable,
+ logTag: String
+) : RecorderCallback(src, logTag) {
/**
* Construct a testable network callback.
* @param timeoutMs the default timeout for expecting a callback. Default 30 seconds. This
@@ -213,14 +219,16 @@
constructor(
timeoutMs: Long = DEFAULT_TIMEOUT,
noCallbackTimeoutMs: Long = DEFAULT_NO_CALLBACK_TIMEOUT,
- waiterFunc: Runnable = NOOP
- ) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc)
+ waiterFunc: Runnable = NOOP, // "() -> Unit" would forbid calling with a void func from Java
+ logTag: String = DEFAULT_TAG
+ ) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc, logTag)
fun createLinkedCopy() = TestableNetworkCallback(
this,
defaultTimeoutMs,
defaultNoCallbackTimeoutMs,
- waiterFunc
+ waiterFunc,
+ logTag
)
// The last available network, or null if any network was lost since the last call to
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt b/staticlibs/testutils/host/java/com/android/testutils/ConnectivityTestTargetPreparer.kt
similarity index 100%
rename from staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
rename to staticlibs/testutils/host/java/com/android/testutils/ConnectivityTestTargetPreparer.kt
diff --git a/staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt b/staticlibs/testutils/host/java/com/android/testutils/DisableConfigSyncTargetPreparer.kt
similarity index 100%
rename from staticlibs/testutils/host/com/android/testutils/DisableConfigSyncTargetPreparer.kt
rename to staticlibs/testutils/host/java/com/android/testutils/DisableConfigSyncTargetPreparer.kt
diff --git a/staticlibs/testutils/host/python/adb_utils.py b/staticlibs/testutils/host/python/adb_utils.py
new file mode 100644
index 0000000..13c0646
--- /dev/null
+++ b/staticlibs/testutils/host/python/adb_utils.py
@@ -0,0 +1,118 @@
+# 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.
+
+import re
+from mobly.controllers import android_device
+from net_tests_utils.host.python import assert_utils
+
+BYTE_DECODE_UTF_8 = "utf-8"
+
+
+def set_doze_mode(ad: android_device.AndroidDevice, enable: bool) -> None:
+ if enable:
+ adb_shell(ad, "cmd battery unplug")
+ expect_dumpsys_state_with_retry(
+ ad, "deviceidle", key="mCharging", expected_state=False
+ )
+ _set_screen_state(ad, False)
+ adb_shell(ad, "dumpsys deviceidle enable deep")
+ expect_dumpsys_state_with_retry(
+ ad, "deviceidle", key="mDeepEnabled", expected_state=True
+ )
+ adb_shell(ad, "dumpsys deviceidle force-idle deep")
+ expect_dumpsys_state_with_retry(
+ ad, "deviceidle", key="mForceIdle", expected_state=True
+ )
+ else:
+ adb_shell(ad, "cmd battery reset")
+ expect_dumpsys_state_with_retry(
+ ad, "deviceidle", key="mCharging", expected_state=True
+ )
+ adb_shell(ad, "dumpsys deviceidle unforce")
+ expect_dumpsys_state_with_retry(
+ ad, "deviceidle", key="mForceIdle", expected_state=False
+ )
+
+
+def _set_screen_state(
+ ad: android_device.AndroidDevice, target_state: bool
+) -> None:
+ assert_utils.expect_with_retry(
+ predicate=lambda: _get_screen_state(ad) == target_state,
+ retry_action=lambda: adb_shell(
+ ad, "input keyevent KEYCODE_POWER"
+ ), # Toggle power key again when retry.
+ )
+
+
+def _get_screen_state(ad: android_device.AndroidDevice) -> bool:
+ return get_value_of_key_from_dumpsys(ad, "power", "mWakefulness") == "Awake"
+
+
+def get_value_of_key_from_dumpsys(
+ ad: android_device.AndroidDevice, service: str, key: str
+) -> str:
+ output = get_dumpsys_for_service(ad, service)
+ # Search for key=value pattern from the dumpsys output.
+ # e.g. mWakefulness=Awake
+ pattern = rf"{key}=(.*)"
+ # Only look for the first occurrence.
+ match = re.search(pattern, output)
+ if match:
+ ad.log.debug(
+ "Getting key-value from dumpsys: " + key + "=" + match.group(1)
+ )
+ return match.group(1)
+ else:
+ return None
+
+
+def expect_dumpsys_state_with_retry(
+ ad: android_device.AndroidDevice,
+ service: str,
+ key: str,
+ expected_state: bool,
+ retry_interval_sec: int = 1,
+) -> None:
+ def predicate():
+ value = get_value_of_key_from_dumpsys(ad, service, key)
+ if value is None:
+ return False
+ return value.lower() == str(expected_state).lower()
+
+ assert_utils.expect_with_retry(
+ predicate=predicate,
+ retry_interval_sec=retry_interval_sec,
+ )
+
+
+def get_dumpsys_for_service(
+ ad: android_device.AndroidDevice, service: str
+) -> str:
+ return adb_shell(ad, "dumpsys " + service)
+
+
+def adb_shell(ad: android_device.AndroidDevice, shell_cmd: str) -> str:
+ """Runs adb shell command.
+
+ Args:
+ ad: Android device object.
+ shell_cmd: string of list of strings, adb shell command.
+
+ Returns:
+ string, replies from adb shell command.
+ """
+ ad.log.debug("Executing adb shell %s", shell_cmd)
+ data = ad.adb.shell(shell_cmd)
+ return data.decode(BYTE_DECODE_UTF_8).strip()
diff --git a/staticlibs/testutils/host/python/apf_utils.py b/staticlibs/testutils/host/python/apf_utils.py
new file mode 100644
index 0000000..415799c
--- /dev/null
+++ b/staticlibs/testutils/host/python/apf_utils.py
@@ -0,0 +1,256 @@
+# 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.
+
+from dataclasses import dataclass
+import re
+from mobly import asserts
+from mobly.controllers import android_device
+from mobly.controllers.android_device_lib.adb import AdbError
+from net_tests_utils.host.python import adb_utils, assert_utils
+
+
+# Constants.
+ETHER_BROADCAST = "FFFFFFFFFFFF"
+ETH_P_ETHERCAT = "88A4"
+
+
+class PatternNotFoundException(Exception):
+ """Raised when the given pattern cannot be found."""
+
+
+class UnsupportedOperationException(Exception):
+ pass
+
+
+def get_apf_counter(
+ ad: android_device.AndroidDevice, iface: str, counter_name: str
+) -> int:
+ counters = get_apf_counters_from_dumpsys(ad, iface)
+ return counters.get(counter_name, 0)
+
+
+def get_apf_counters_from_dumpsys(
+ ad: android_device.AndroidDevice, iface_name: str
+) -> dict:
+ dumpsys = adb_utils.get_dumpsys_for_service(ad, "network_stack")
+
+ # Extract IpClient section of the specified interface.
+ # This takes inputs like:
+ # IpClient.wlan0
+ # ...
+ # IpClient.wlan1
+ # ...
+ iface_pattern = re.compile(
+ r"^IpClient\." + iface_name + r"\n" + r"((^\s.*\n)+)", re.MULTILINE
+ )
+ iface_result = iface_pattern.search(dumpsys)
+ if iface_result is None:
+ raise PatternNotFoundException("Cannot find IpClient for " + iface_name)
+
+ # Extract APF counters section from IpClient section, which looks like:
+ # APF packet counters:
+ # COUNTER_NAME: VALUE
+ # ....
+ apf_pattern = re.compile(
+ r"APF packet counters:.*\n.(\s+[A-Z_0-9]+: \d+\n)+", re.MULTILINE
+ )
+ apf_result = apf_pattern.search(iface_result.group(0))
+ if apf_result is None:
+ raise PatternNotFoundException(
+ "Cannot find APF counters in text: " + iface_result.group(0)
+ )
+
+ # Extract key-value pairs from APF counters section into a list of tuples,
+ # e.g. [('COUNTER1', '1'), ('COUNTER2', '2')].
+ counter_pattern = re.compile(r"(?P<name>[A-Z_0-9]+): (?P<value>\d+)")
+ counter_result = counter_pattern.findall(apf_result.group(0))
+ if counter_result is None:
+ raise PatternNotFoundException(
+ "Cannot extract APF counters in text: " + apf_result.group(0)
+ )
+
+ # Convert into a dict.
+ result = {}
+ for key, value_str in counter_result:
+ result[key] = int(value_str)
+
+ ad.log.debug("Getting apf counters: " + str(result))
+ return result
+
+
+def get_hardware_address(
+ ad: android_device.AndroidDevice, iface_name: str
+) -> str:
+ """Retrieves the hardware (MAC) address for a given network interface.
+
+ Returns:
+ The hex representative of the MAC address in uppercase.
+ E.g. 12:34:56:78:90:AB
+
+ Raises:
+ PatternNotFoundException: If the MAC address is not found in the command
+ output.
+ """
+
+ # Run the "ip link" command and get its output.
+ ip_link_output = adb_utils.adb_shell(ad, f"ip link show {iface_name}")
+
+ # Regular expression to extract the MAC address.
+ # Parse hardware address from ip link output like below:
+ # 46: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq ...
+ # link/ether 72:05:77:82:21:e0 brd ff:ff:ff:ff:ff:ff
+ pattern = r"link/ether (([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})"
+ match = re.search(pattern, ip_link_output)
+
+ if match:
+ return match.group(1).upper() # Extract the MAC address string.
+ else:
+ raise PatternNotFoundException(
+ "Cannot get hardware address for " + iface_name
+ )
+
+
+def send_broadcast_empty_ethercat_packet(
+ ad: android_device.AndroidDevice, iface_name: str
+) -> None:
+ """Transmits a broadcast empty EtherCat packet on the specified interface."""
+
+ # Get the interface's MAC address.
+ mac_address = get_hardware_address(ad, iface_name)
+
+ # TODO: Build packet by using scapy library.
+ # Ethernet header (14 bytes).
+ packet = ETHER_BROADCAST # Destination MAC (broadcast)
+ packet += mac_address.replace(":", "") # Source MAC
+ packet += ETH_P_ETHERCAT # EtherType (EtherCAT)
+
+ # EtherCAT header (2 bytes) + 44 bytes of zero padding.
+ packet += "00" * 46
+
+ # Send the packet using a raw socket.
+ send_raw_packet_downstream(ad, iface_name, packet)
+
+
+def send_raw_packet_downstream(
+ ad: android_device.AndroidDevice,
+ iface_name: str,
+ packet_in_hex: str,
+) -> None:
+ """Sends a raw packet over the specified downstream interface.
+
+ This function constructs and sends a raw packet using the
+ `send-raw-packet-downstream`
+ command provided by NetworkStack process. It's primarily intended for testing
+ purposes.
+
+ Args:
+ ad: The AndroidDevice object representing the connected device.
+ iface_name: The name of the network interface to use (e.g., "wlan0",
+ "eth0").
+ packet_in_hex: The raw packet data starting from L2 header encoded in
+ hexadecimal string format.
+
+ Raises:
+ UnsupportedOperationException: If the NetworkStack doesn't support
+ the `send-raw-packet` command.
+ UnexpectedBehaviorException: If the command execution produces unexpected
+ output other than an empty response or "Unknown command".
+
+ Important Considerations:
+ Security: This method only works on tethering downstream interfaces due
+ to security restrictions.
+ Packet Format: The `packet_in_hex` must be a valid hexadecimal
+ representation of a packet starting from L2 header.
+ """
+
+ cmd = (
+ "cmd network_stack send-raw-packet-downstream"
+ f" {iface_name} {packet_in_hex}"
+ )
+
+ # Expect no output or Unknown command if NetworkStack is too old. Throw otherwise.
+ try:
+ output = adb_utils.adb_shell(ad, cmd)
+ except AdbError as e:
+ output = str(e.stdout)
+ if output:
+ if "Unknown command" in output:
+ raise UnsupportedOperationException(
+ "send-raw-packet-downstream command is not supported."
+ )
+ raise assert_utils.UnexpectedBehaviorError(
+ f"Got unexpected output: {output} for command: {cmd}."
+ )
+
+
+@dataclass
+class ApfCapabilities:
+ """APF program support capabilities.
+
+ See android.net.apf.ApfCapabilities.
+
+ Attributes:
+ apf_version_supported (int): Version of APF instruction set supported for
+ packet filtering. 0 indicates no support for packet filtering using APF
+ programs.
+ apf_ram_size (int): Size of APF ram.
+ apf_packet_format (int): Format of packets passed to APF filter. Should be
+ one of ARPHRD_*
+ """
+
+ apf_version_supported: int
+ apf_ram_size: int
+ apf_packet_format: int
+
+ def __init__(
+ self,
+ apf_version_supported: int,
+ apf_ram_size: int,
+ apf_packet_format: int,
+ ):
+ self.apf_version_supported = apf_version_supported
+ self.apf_ram_size = apf_ram_size
+ self.apf_packet_format = apf_packet_format
+
+ def __str__(self):
+ """Returns a user-friendly string representation of the APF capabilities."""
+ return (
+ f"APF Version: {self.apf_version_supported}\n"
+ f"Ram Size: {self.apf_ram_size} bytes\n"
+ f"Packet Format: {self.apf_packet_format}"
+ )
+
+
+def get_apf_capabilities(
+ ad: android_device.AndroidDevice, iface_name: str
+) -> ApfCapabilities:
+ output = adb_utils.adb_shell(
+ ad, f"cmd network_stack apf {iface_name} capabilities"
+ )
+ try:
+ values = [int(value_str) for value_str in output.split(",")]
+ except ValueError:
+ return ApfCapabilities(0, 0, 0) # Conversion to integer failed
+ return ApfCapabilities(values[0], values[1], values[2])
+
+
+def assume_apf_version_support_at_least(
+ ad: android_device.AndroidDevice, iface_name: str, expected_version: int
+) -> None:
+ caps = get_apf_capabilities(ad, iface_name)
+ asserts.skip_if(
+ caps.apf_version_supported < expected_version,
+ f"Supported apf version {caps.apf_version_supported} < expected version"
+ f" {expected_version}",
+ )
diff --git a/staticlibs/testutils/host/python/assert_utils.py b/staticlibs/testutils/host/python/assert_utils.py
new file mode 100644
index 0000000..da1bb9e
--- /dev/null
+++ b/staticlibs/testutils/host/python/assert_utils.py
@@ -0,0 +1,43 @@
+# 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.
+
+import time
+from typing import Callable
+
+
+class UnexpectedBehaviorError(Exception):
+ """Raised when there is an unexpected behavior during applying a procedure."""
+
+
+def expect_with_retry(
+ predicate: Callable[[], bool],
+ retry_action: Callable[[], None] = None,
+ max_retries: int = 10,
+ retry_interval_sec: int = 1,
+) -> None:
+ """Executes a predicate and retries if it doesn't return True."""
+
+ for retry in range(max_retries):
+ if predicate():
+ return None
+ else:
+ if retry == max_retries - 1:
+ break
+ if retry_action:
+ retry_action()
+ time.sleep(retry_interval_sec)
+
+ raise UnexpectedBehaviorError(
+ "Predicate didn't become true after " + str(max_retries) + " retries."
+ )
diff --git a/tests/cts/multidevices/utils/mdns_utils.py b/staticlibs/testutils/host/python/mdns_utils.py
similarity index 76%
rename from tests/cts/multidevices/utils/mdns_utils.py
rename to staticlibs/testutils/host/python/mdns_utils.py
index ec1fea0..1234e54 100644
--- a/tests/cts/multidevices/utils/mdns_utils.py
+++ b/staticlibs/testutils/host/python/mdns_utils.py
@@ -12,9 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from mobly import asserts
from mobly.controllers import android_device
+def assume_mdns_test_preconditions(
+ advertising_device: android_device, discovery_device: android_device
+) -> None:
+ advertising = advertising_device.connectivity_multi_devices_snippet
+ discovery = discovery_device.connectivity_multi_devices_snippet
+
+ asserts.skip_if(
+ not advertising.isAtLeastT(), "Advertising device SDK is lower than T."
+ )
+ asserts.skip_if(
+ not discovery.isAtLeastT(), "Discovery device SDK is lower than T."
+ )
+
+
def register_mdns_service_and_discover_resolve(
advertising_device: android_device, discovery_device: android_device
) -> None:
diff --git a/tests/cts/multidevices/utils/tether_utils.py b/staticlibs/testutils/host/python/tether_utils.py
similarity index 100%
rename from tests/cts/multidevices/utils/tether_utils.py
rename to staticlibs/testutils/host/python/tether_utils.py
diff --git a/staticlibs/testutils/host/python/wifip2p_utils.py b/staticlibs/testutils/host/python/wifip2p_utils.py
new file mode 100644
index 0000000..8b4ffa5
--- /dev/null
+++ b/staticlibs/testutils/host/python/wifip2p_utils.py
@@ -0,0 +1,50 @@
+# 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.
+
+from mobly import asserts
+from mobly.controllers import android_device
+
+
+def assume_wifi_p2p_test_preconditions(
+ server_device: android_device, client_device: android_device
+) -> None:
+ server = server_device.connectivity_multi_devices_snippet
+ client = client_device.connectivity_multi_devices_snippet
+
+ # Assert pre-conditions
+ asserts.skip_if(not server.hasWifiFeature(), "Server requires Wifi feature")
+ asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
+ asserts.skip_if(
+ not server.isP2pSupported(), "Server requires Wi-fi P2P feature"
+ )
+ asserts.skip_if(
+ not client.isP2pSupported(), "Client requires Wi-fi P2P feature"
+ )
+
+
+def setup_wifi_p2p_server_and_client(
+ server_device: android_device, client_device: android_device
+) -> None:
+ """Set up the Wi-Fi P2P server and client."""
+ # Start Wi-Fi P2P on both server and client.
+ server_device.connectivity_multi_devices_snippet.startWifiP2p()
+ client_device.connectivity_multi_devices_snippet.startWifiP2p()
+
+
+def cleanup_wifi_p2p(
+ server_device: android_device, client_device: android_device
+) -> None:
+ # Stop Wi-Fi P2P
+ server_device.connectivity_multi_devices_snippet.stopWifiP2p()
+ client_device.connectivity_multi_devices_snippet.stopWifiP2p()
diff --git a/tests/cts/hostside-network-policy/Android.bp b/tests/cts/hostside-network-policy/Android.bp
deleted file mode 100644
index c3ce0b9..0000000
--- a/tests/cts/hostside-network-policy/Android.bp
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 {
- default_team: "trendy_team_framework_backstage_power",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_test_host {
- name: "CtsHostsideNetworkPolicyTests",
- defaults: ["cts_defaults"],
- // Only compile source java files in this apk.
- srcs: [
- "src/**/*.java",
- ":ArgumentConstants",
- ],
- libs: [
- "cts-tradefed",
- "tradefed",
- ],
- static_libs: [
- "modules-utils-build-testing",
- ],
- // Tag this module as a cts test artifact
- test_suites: [
- "cts",
- "general-tests",
- "sts",
- ],
- data: [
- ":CtsHostsideNetworkPolicyTestsApp",
- ":CtsHostsideNetworkPolicyTestsApp2",
- ],
- per_testcase_directory: true,
-}
diff --git a/tests/cts/hostside-network-policy/AndroidTest.xml b/tests/cts/hostside-network-policy/AndroidTest.xml
deleted file mode 100644
index 44f77f8..0000000
--- a/tests/cts/hostside-network-policy/AndroidTest.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for CTS network policy host test cases">
- <option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="networking" />
- <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
- <target_preparer class="com.android.cts.netpolicy.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.netpolicy.hostside.app2" />
- <option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.netpolicy.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" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <option name="force-skip-system-props" value="true" />
- <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
- <option name="set-global-setting" key="low_power_standby_enabled" value="0" />
- </target_preparer>
-
- <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="CtsHostsideNetworkPolicyTests.jar" />
- <option name="runtime-hint" value="3m56s" />
- </test>
-
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/CtsHostsideNetworkPolicyTests" />
- <option name="collect-on-run-ended-only" value="true" />
- </metrics_collector>
-</configuration>
diff --git a/tests/cts/hostside-network-policy/OWNERS b/tests/cts/hostside-network-policy/OWNERS
deleted file mode 100644
index ea83e61..0000000
--- a/tests/cts/hostside-network-policy/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 61373
-# Inherits parent owners
-include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/tests/cts/hostside-network-policy/TEST_MAPPING b/tests/cts/hostside-network-policy/TEST_MAPPING
deleted file mode 100644
index 57ac4f7..0000000
--- a/tests/cts/hostside-network-policy/TEST_MAPPING
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "presubmit-large": [
- {
- "name": "CtsHostsideNetworkPolicyTests",
- "options": [
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
- }
- ]
- }
- ],
- "postsubmit": [
- {
- // Postsubmit on virtual devices to monitor flakiness of all tests that don't require a
- // physical device
- "name": "CtsHostsideNetworkPolicyTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.RequiresDevice"
- }
- ]
- }
- ]
-}
diff --git a/tests/cts/hostside-network-policy/aidl/Android.bp b/tests/cts/hostside-network-policy/aidl/Android.bp
deleted file mode 100644
index b182090..0000000
--- a/tests/cts/hostside-network-policy/aidl/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 {
- default_team: "trendy_team_framework_backstage_power",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_test_helper_library {
- name: "CtsHostsideNetworkPolicyTestsAidl",
- sdk_version: "current",
- srcs: [
- "com/android/cts/netpolicy/hostside/*.aidl",
- ],
-}
diff --git a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
deleted file mode 100644
index 068d9d8..0000000
--- a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import android.app.job.JobInfo;
-
-import com.android.cts.netpolicy.hostside.INetworkCallback;
-import com.android.cts.netpolicy.hostside.NetworkCheckResult;
-
-interface IMyService {
- void registerBroadcastReceiver();
- int getCounters(String receiverName, String action);
- NetworkCheckResult checkNetworkStatus(String customUrl);
- String getRestrictBackgroundStatus();
- void sendNotification(int notificationId, String notificationType);
- void registerNetworkCallback(in NetworkRequest request, in INetworkCallback cb);
- void unregisterNetworkCallback();
- int scheduleJob(in JobInfo jobInfo);
-}
diff --git a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
deleted file mode 100644
index 38efc7b..0000000
--- a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import android.net.Network;
-import android.net.NetworkCapabilities;
-
-interface INetworkCallback {
- void onBlockedStatusChanged(in Network network, boolean blocked);
- void onAvailable(in Network network);
- void onLost(in Network network);
- void onCapabilitiesChanged(in Network network, in NetworkCapabilities cap);
-}
diff --git a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
deleted file mode 100644
index c6b7a1c..0000000
--- a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import android.net.NetworkInfo;
-
-import com.android.cts.netpolicy.hostside.NetworkCheckResult;
-
-interface INetworkStateObserver {
- void onNetworkStateChecked(int resultCode, in NetworkCheckResult networkCheckResult);
-
- 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-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
deleted file mode 100644
index 7aac2ab..0000000
--- a/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.netpolicy.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-network-policy/app/Android.bp b/tests/cts/hostside-network-policy/app/Android.bp
deleted file mode 100644
index a31c843..0000000
--- a/tests/cts/hostside-network-policy/app/Android.bp
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-// 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 {
- default_team: "trendy_team_framework_backstage_power",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_defaults {
- name: "CtsHostsideNetworkPolicyTestsAppDefaults",
- platform_apis: true,
- static_libs: [
- "CtsHostsideNetworkPolicyTestsAidl",
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "androidx.test.uiautomator_uiautomator",
- "compatibility-device-util-axt",
- "cts-net-utils",
- "ctstestrunner-axt",
- "modules-utils-build",
- ],
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
- srcs: [
- "src/**/*.java",
- ":ArgumentConstants",
- ],
- // Tag this module as a cts test artifact
- test_suites: [
- "general-tests",
- "sts",
- ],
-}
-
-android_test_helper_app {
- name: "CtsHostsideNetworkPolicyTestsApp",
- defaults: [
- "cts_support_defaults",
- "framework-connectivity-test-defaults",
- "CtsHostsideNetworkPolicyTestsAppDefaults",
- ],
-}
diff --git a/tests/cts/hostside-network-policy/app/AndroidManifest.xml b/tests/cts/hostside-network-policy/app/AndroidManifest.xml
deleted file mode 100644
index f19e35f..0000000
--- a/tests/cts/hostside-network-policy/app/AndroidManifest.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.netpolicy.hostside">
-
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WAKE_LOCK" />
-
- <application android:requestLegacyExternalStorage="true">
- <uses-library android:name="android.test.runner"/>
- <service android:name=".MyNotificationListenerService"
- android:label="MyNotificationListenerService"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
- android:exported="true">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService"/>
- </intent-filter>
- </service>
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.netpolicy.hostside"/>
-
-</manifest>
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
deleted file mode 100644
index 19e4364..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.SystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Base class for metered and non-metered tests on idle apps.
- */
-@RequiredProperties({APP_STANDBY_MODE})
-abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
-
- @Before
- public final void setUp() throws Exception {
- super.setUp();
-
- // Set initial state.
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- setAppIdle(false);
- turnBatteryOn();
-
- registerBroadcastReceiver();
- }
-
- @After
- public final void tearDown() throws Exception {
- super.tearDown();
-
- resetBatteryState();
- setAppIdle(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_enabled() throws Exception {
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- // Make sure foreground app doesn't lose access upon enabling it.
- setAppIdle(true);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- finishActivity();
- assertAppIdle(false); // verify - not idle anymore, since activity was launched...
- assertBackgroundNetworkAccess(true);
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- // Same for foreground service.
- setAppIdle(true);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- stopForegroundService();
- assertAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- // Set Idle after foreground service start.
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- setAppIdle(true);
- addPowerSaveModeWhitelist(TEST_PKG);
- removePowerSaveModeWhitelist(TEST_PKG);
- assertForegroundServiceNetworkAccess();
- stopForegroundService();
- assertAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- }
-
- @Test
- public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertAppIdle(false); // verify - not idle anymore, since whitelisted
- assertBackgroundNetworkAccess(true);
-
- setAppIdleNoAssert(true);
- assertAppIdle(false); // app is still whitelisted
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertAppIdle(true); // verify - idle again, once whitelisted was removed
- assertBackgroundNetworkAccess(false);
-
- setAppIdle(true);
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertAppIdle(false); // verify - not idle anymore, since whitelisted
- assertBackgroundNetworkAccess(true);
-
- setAppIdleNoAssert(true);
- assertAppIdle(false); // app is still whitelisted
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertAppIdle(true); // verify - idle again, once whitelisted was removed
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
-
- // verify - no whitelist, no access!
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(true);
- // Wait until the whitelist duration is expired.
- SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_disabled() throws Exception {
- assertBackgroundNetworkAccess(true);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- }
-
- @RequiredProperties({BATTERY_SAVER_MODE})
- @Test
- public void testAppIdleNetworkAccess_whenCharging() throws Exception {
- // Check that app is paroled when charging
- setAppIdle(true);
- assertBackgroundNetworkAccess(false);
- turnBatteryOff();
- assertBackgroundNetworkAccess(true);
- turnBatteryOn();
- assertBackgroundNetworkAccess(false);
-
- // Check that app is restricted when not idle but power-save is on
- setAppIdle(false);
- assertBackgroundNetworkAccess(true);
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
- // Use setBatterySaverMode API to leave power-save mode instead of plugging in charger
- setBatterySaverMode(false);
- turnBatteryOff();
- assertBackgroundNetworkAccess(true);
-
- // And when no longer charging, it still has network access, since it's not idle
- turnBatteryOn();
- assertBackgroundNetworkAccess(true);
- }
-
- @Test
- public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
- setAppIdle(true);
- assertAppIdle(true);
- assertBackgroundNetworkAccess(false);
-
- addAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
-
- removeAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
-
- // Make sure whitelisting a random app doesn't affect the tested app.
- addAppIdleWhitelist(mUid + 1);
- assertBackgroundNetworkAccess(false);
- removeAppIdleWhitelist(mUid + 1);
- }
-
- @Test
- public void testAppIdle_toast() throws Exception {
- setAppIdle(true);
- assertAppIdle(true);
- assertEquals("Shown", showToast());
- assertAppIdle(true);
- // Wait for a couple of seconds for the toast to actually be shown
- SystemClock.sleep(2000);
- assertAppIdle(true);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
deleted file mode 100644
index ae226e2..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Base class for metered and non-metered Battery Saver Mode tests.
- */
-@RequiredProperties({BATTERY_SAVER_MODE})
-abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
-
- @Before
- public final void setUp() throws Exception {
- super.setUp();
-
- // Set initial state.
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- setBatterySaverMode(false);
-
- registerBroadcastReceiver();
- }
-
- @After
- public final void tearDown() throws Exception {
- super.tearDown();
-
- setBatterySaverMode(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_enabled() throws Exception {
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- // Make sure foreground app doesn't lose access upon Battery Saver.
- setBatterySaverMode(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- setBatterySaverMode(true);
- assertTopNetworkAccess(true);
-
- // Although it should not have access while the screen is off.
- turnScreenOff();
- assertBackgroundNetworkAccess(false);
- turnScreenOn();
- assertTopNetworkAccess(true);
-
- // Goes back to background state.
- finishActivity();
- assertBackgroundNetworkAccess(false);
-
- // Make sure foreground service doesn't lose access upon enabling Battery Saver.
- setBatterySaverMode(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- setBatterySaverMode(true);
- assertForegroundServiceNetworkAccess();
- stopForegroundService();
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_disabled() throws Exception {
- assertBackgroundNetworkAccess(true);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
deleted file mode 100644
index 00f67f4..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
-
-import static org.junit.Assume.assumeTrue;
-
-import android.os.SystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Base class for default, always-on network restrictions.
- */
-abstract class AbstractDefaultRestrictionsTest extends AbstractRestrictBackgroundNetworkTestCase {
-
- @Before
- public final void setUp() throws Exception {
- super.setUp();
-
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
-
- registerBroadcastReceiver();
- assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
- }
-
- @After
- public final void tearDown() throws Exception {
- super.tearDown();
-
- stopApp();
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- }
-
- @Test
- public void testFgsNetworkAccess() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- SystemClock.sleep(mProcessStateTransitionShortDelayMs);
- assertNetworkAccess(false, null);
-
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- }
-
- @Test
- public void testActivityNetworkAccess() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- SystemClock.sleep(mProcessStateTransitionShortDelayMs);
- assertNetworkAccess(false, null);
-
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- }
-
- @Test
- public void testBackgroundNetworkAccess_inFullAllowlist() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- SystemClock.sleep(mProcessStateTransitionShortDelayMs);
- assertNetworkAccess(false, null);
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- assertNetworkAccess(true, null);
- }
-
- @Test
- public void testBackgroundNetworkAccess_inExceptIdleAllowlist() throws Exception {
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- SystemClock.sleep(mProcessStateTransitionShortDelayMs);
- assertNetworkAccess(false, null);
-
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- assertNetworkAccess(true, null);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
deleted file mode 100644
index 0c8cb70..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-
-import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
-import static com.android.cts.netpolicy.hostside.Property.NOT_LOW_RAM_DEVICE;
-
-import android.os.SystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Base class for metered and non-metered Doze Mode tests.
- */
-@RequiredProperties({DOZE_MODE})
-abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
-
- @Before
- public final void setUp() throws Exception {
- super.setUp();
-
- // Set initial state.
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- setDozeMode(false);
-
- registerBroadcastReceiver();
- }
-
- @After
- public final void tearDown() throws Exception {
- super.tearDown();
-
- setDozeMode(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_enabled() throws Exception {
- setDozeMode(true);
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- // Make sure foreground service doesn't lose network access upon enabling doze.
- setDozeMode(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- setDozeMode(true);
- assertForegroundServiceNetworkAccess();
- stopForegroundService();
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- setDozeMode(true);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testBackgroundNetworkAccess_disabled() throws Exception {
- assertBackgroundNetworkAccess(true);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- }
-
- @RequiredProperties({NOT_LOW_RAM_DEVICE})
- @Test
- public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
- throws Exception {
- setPendingIntentAllowlistDuration(NETWORK_TIMEOUT_MS);
- try {
- registerNotificationListenerService();
- setDozeMode(true);
- assertBackgroundNetworkAccess(false);
-
- testNotification(4, NOTIFICATION_TYPE_CONTENT);
- testNotification(8, NOTIFICATION_TYPE_DELETE);
- testNotification(15, NOTIFICATION_TYPE_FULL_SCREEN);
- testNotification(16, NOTIFICATION_TYPE_BUNDLE);
- testNotification(23, NOTIFICATION_TYPE_ACTION);
- testNotification(42, NOTIFICATION_TYPE_ACTION_BUNDLE);
- testNotification(108, NOTIFICATION_TYPE_ACTION_REMOTE_INPUT);
- } finally {
- resetDeviceIdleSettings();
- }
- }
-
- private void testNotification(int id, String type) throws Exception {
- sendNotification(id, type);
- assertBackgroundNetworkAccess(true);
- if (type.equals(NOTIFICATION_TYPE_ACTION)) {
- // Make sure access is disabled after it expires. Since this check considerably slows
- // downs the CTS tests, do it just once.
- SystemClock.sleep(NETWORK_TIMEOUT_MS);
- assertBackgroundNetworkAccess(false);
- }
- }
-
- // Must override so it only tests foreground service - once an app goes to foreground, device
- // leaves Doze Mode.
- @Override
- protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- stopForegroundService();
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
deleted file mode 100644
index 5435920..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
-import static com.android.cts.netpolicy.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();
- setAppIdle(false);
- 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-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
deleted file mode 100644
index 0f5f58c..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ /dev/null
@@ -1,1165 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
-import static android.app.job.JobScheduler.RESULT_SUCCESS;
-import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
-
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.executeShellCommand;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.forceRunJob;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getConnectivityManager;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getContext;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getInstrumentation;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackgroundInternal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.annotation.NonNull;
-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;
-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;
-import android.os.BatteryManager;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.PowerManager;
-import android.os.RemoteCallback;
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.service.notification.NotificationListenerService;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.Nullable;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.AmUtils;
-import com.android.compatibility.common.util.BatteryUtils;
-import com.android.compatibility.common.util.DeviceConfigStateHelper;
-import com.android.compatibility.common.util.ThrowingRunnable;
-import com.android.modules.utils.build.SdkLevel;
-
-import org.junit.Rule;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-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;
-
-/**
- * Superclass for tests related to background network restrictions.
- */
-@RunWith(NetworkPolicyTestRunner.class)
-public abstract class AbstractRestrictBackgroundNetworkTestCase {
- public static final String TAG = "RestrictBackgroundNetworkTests";
-
- protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
- protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
- // TODO(b/321797685): Configure it via device-config once it is available.
- protected final long mProcessStateTransitionLongDelayMs =
- useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(20)
- : TimeUnit.SECONDS.toMillis(5);
- protected final long mProcessStateTransitionShortDelayMs =
- useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(2)
- : TimeUnit.SECONDS.toMillis(5);
-
- 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.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
- private static final String ACTION_FINISH_JOB =
- "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
- // Copied from com.android.server.net.NetworkPolicyManagerService class
- private static final String ACTION_SNOOZE_WARNING =
- "com.android.server.net.action.SNOOZE_WARNING";
-
- private static final String ACTION_RECEIVER_READY =
- "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
- static final String ACTION_SHOW_TOAST =
- "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
-
- protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
- protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
- protected static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
- protected static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
- protected static final String NOTIFICATION_TYPE_ACTION = "ACTION";
- protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
- protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
-
- 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 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 = "";
-
- 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 ACTIVITY_NETWORK_STATE_TIMEOUT_MS = 10_000;
- private static final int JOB_NETWORK_STATE_TIMEOUT_MS = 10_000;
- private static final int LAUNCH_ACTIVITY_TIMEOUT_MS = 10_000;
-
- // Must be higher than NETWORK_TIMEOUT_MS
- private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
-
- private static final IntentFilter BATTERY_CHANGED_FILTER =
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
-
- protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 20_000; // 20 sec
-
- private static final long BROADCAST_TIMEOUT_MS = 5_000;
-
- protected Context mContext;
- protected Instrumentation mInstrumentation;
- protected ConnectivityManager mCm;
- protected int mUid;
- private int mMyUid;
- private @Nullable String mCustomUrl;
- private MyServiceClient mServiceClient;
- private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper;
- private PowerManager mPowerManager;
- private PowerManager.WakeLock mLock;
-
- @Rule
- public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule())
- .around(new MeterednessConfigurationRule());
-
- protected void setUp() throws Exception {
- 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);
-
- 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;
- } else {
- bindPriorityFlags = Context.BIND_NOT_FOREGROUND;
- }
- mServiceClient.bind(bindPriorityFlags);
-
- mPowerManager = mContext.getSystemService(PowerManager.class);
- executeShellCommand("cmd netpolicy start-watching " + mUid);
- // Some of the test cases assume that Data saver mode is initially disabled, which might not
- // always be the case. Therefore, explicitly disable it before running the tests.
- // Invoke setRestrictBackgroundInternal() directly instead of going through
- // setRestrictBackground(), as some devices do not fully support the Data saver mode but
- // still have certain parts of it enabled by default.
- setRestrictBackgroundInternal(false);
- setAppIdle(false);
- mLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- Log.i(TAG, "Apps status:\n"
- + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
- + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
- }
-
- protected void tearDown() throws Exception {
- executeShellCommand("cmd netpolicy stop-watching");
- mServiceClient.unbind();
- final PowerManager.WakeLock lock = mLock;
- if (null != lock && lock.isHeld()) lock.release();
- }
-
- /**
- * Check if the feature blocking network for top_sleeping and lower priority proc-states is
- * enabled. This is a manual check because the feature flag infrastructure may not be available
- * in all the branches that will get this code.
- * TODO: b/322115994 - Use @RequiresFlagsEnabled with
- * Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE once the tests are moved to cts.
- */
- protected boolean isNetworkBlockedForTopSleepingAndAbove() {
- if (!SdkLevel.isAtLeastV()) {
- return false;
- }
- final String output = executeShellCommand("device_config get backstage_power"
- + " com.android.server.net.network_blocked_for_top_sleeping_and_above");
- return Boolean.parseBoolean(output);
- }
-
- /**
- * Check if the flag to use different delays for sensitive proc-states is enabled.
- * This is a manual check because the feature flag infrastructure may not be available
- * in all the branches that will get this code.
- * TODO: b/322115994 - Use @RequiresFlagsEnabled with
- * Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN once the tests are moved to cts.
- */
- private boolean useDifferentDelaysForBackgroundChain() {
- if (!SdkLevel.isAtLeastV()) {
- return false;
- }
- final String output = executeShellCommand("device_config get backstage_power"
- + " com.android.server.net.use_different_delays_for_background_chain");
- return Boolean.parseBoolean(output);
- }
-
- protected int getUid(String packageName) throws Exception {
- return mContext.getPackageManager().getPackageUid(packageName, 0);
- }
-
- protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception {
- assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount);
- assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0);
- }
-
- protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)
- throws Exception {
- int attempts = 0;
- int count = 0;
- final int maxAttempts = 5;
- do {
- attempts++;
- count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
- assertFalse("Expected count " + expectedCount + " but actual is " + count,
- count > expectedCount);
- if (count == expectedCount) {
- break;
- }
- Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
- + attempts + " attempts; sleeping "
- + SLEEP_TIME_SEC + " seconds before trying again");
- // No sleep after the last turn
- if (attempts <= maxAttempts) {
- SystemClock.sleep(SLEEP_TIME_SEC * SECOND_IN_MS);
- }
- } while (attempts <= maxAttempts);
- assertEquals("Number of expected broadcasts for " + receiverName + " not reached after "
- + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count);
- }
-
- protected void assertSnoozeWarningNotReceived() throws Exception {
- // Wait for a while to take broadcast queue delays into account
- SystemClock.sleep(BROADCAST_TIMEOUT_MS);
- assertEquals(0, getNumberBroadcastsReceived(DYNAMIC_RECEIVER, ACTION_SNOOZE_WARNING));
- }
-
- protected String sendOrderedBroadcast(Intent intent) throws Exception {
- return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS);
- }
-
- protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception {
- final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
- Log.d(TAG, "Sending ordered broadcast: " + intent);
- mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String resultData = getResultData();
- if (resultData == null) {
- Log.e(TAG, "Received null data from ordered intent");
- // Offer an empty string so that the code waiting for the result can return.
- result.offer(EMPTY_STRING);
- return;
- }
- result.offer(resultData);
- }
- }, null, 0, null, null);
-
- final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
- Log.d(TAG, "Ordered broadcast response after " + timeoutMs + "ms: " + resultData );
- return resultData;
- }
-
- protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception {
- return mServiceClient.getCounters(receiverName, action);
- }
-
- protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
- final String status = mServiceClient.getRestrictBackgroundStatus();
- assertNotNull("didn't get API status from app2", status);
- assertEquals(restrictBackgroundValueToString(expectedStatus),
- restrictBackgroundValueToString(Integer.parseInt(status)));
- }
-
- /**
- * @deprecated The definition of "background" can be ambiguous. Use separate calls to
- * {@link #assertProcessStateBelow(int)} with
- * {@link #assertNetworkAccess(boolean, boolean, String)} to be explicit, instead.
- */
- @Deprecated
- protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- assertNetworkAccess(expectAllowed, false, null);
- }
-
- protected void assertTopNetworkAccess(boolean expectAllowed) throws Exception {
- assertTopState();
- assertNetworkAccess(expectAllowed, true /* needScreenOn */);
- }
-
- protected void assertForegroundServiceNetworkAccess() throws Exception {
- assertForegroundServiceState();
- assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */);
- }
-
- /**
- * Asserts that an app always have access while on foreground or running a foreground service.
- *
- * <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.
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- finishActivity();
-
- // Then foreground service
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- stopForegroundService();
- }
-
- protected void assertExpeditedJobHasNetworkAccess() throws Exception {
- launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB);
- finishExpeditedJob();
- }
-
- protected void assertExpeditedJobHasNoNetworkAccess() throws Exception {
- launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false);
- finishExpeditedJob();
- }
-
- /**
- * Asserts that the process state of the test app is below, in priority, to the given
- * {@link android.app.ActivityManager.ProcessState}.
- */
- protected final void assertProcessStateBelow(int processState) throws Exception {
- assertProcessState(ps -> ps.state > processState, null);
- }
-
- protected final void assertTopState() throws Exception {
- assertProcessState(ps -> ps.state == PROCESS_STATE_TOP, () -> turnScreenOn());
- }
-
- protected final void assertForegroundServiceState() throws Exception {
- assertProcessState(ps -> ps.state == PROCESS_STATE_FOREGROUND_SERVICE, null);
- }
-
- private void assertProcessState(Predicate<ProcessState> statePredicate,
- ThrowingRunnable onRetry) throws Exception {
- final int maxTries = 30;
- ProcessState state = null;
- for (int i = 1; i <= maxTries; i++) {
- if (onRetry != null) {
- onRetry.run();
- }
- state = getProcessStateByUid(mUid);
- Log.v(TAG, "assertProcessState(): status for app2 (" + mUid + ") on attempt #" + i
- + ": " + state);
- if (statePredicate.test(state)) {
- return;
- }
- Log.i(TAG, "App not in desired process state on attempt #" + i
- + "; sleeping 1s before trying again");
- if (i < maxTries) {
- SystemClock.sleep(SECOND_IN_MS);
- }
- }
- fail("App2 (" + mUid + ") is not in the desired process state after " + maxTries
- + " attempts: " + state);
- }
-
- /**
- * Asserts whether the active network is available or not. If the network is unavailable, also
- * checks whether it is blocked by the expected error.
- *
- * @param expectAllowed expect background network access to be allowed or not.
- * @param expectedUnavailableError the expected error when {@code expectAllowed} is false. It's
- * meaningful only when the {@code expectAllowed} is 'false'.
- * Throws an IllegalArgumentException when {@code expectAllowed}
- * is true and this parameter is not null. When the
- * {@code expectAllowed} is 'false' and this parameter is null,
- * this function does not compare error type of the networking
- * access failure.
- */
- protected void assertNetworkAccess(boolean expectAllowed, String expectedUnavailableError)
- throws Exception {
- if (expectAllowed && expectedUnavailableError != null) {
- throw new IllegalArgumentException("expectedUnavailableError is not null");
- }
- assertNetworkAccess(expectAllowed, false, expectedUnavailableError);
- }
-
- /**
- * Asserts whether the active network is available or not.
- */
- private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn)
- throws Exception {
- assertNetworkAccess(expectAvailable, needScreenOn, null);
- }
-
- private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn,
- @Nullable final String expectedUnavailableError) throws Exception {
- final int maxTries = 5;
- String error = null;
- int timeoutMs = 500;
-
- for (int i = 1; i <= maxTries; i++) {
- error = checkNetworkAccess(expectAvailable, expectedUnavailableError);
-
- 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
- // battery saver tests because the netd changes are made asynchronously.
- // Once b/27803922 is fixed, this retry mechanism should be revisited.
-
- Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable
- + " on attempt #" + i + ": " + error + "\n"
- + "Sleeping " + timeoutMs + "ms before trying again");
- if (needScreenOn) {
- turnScreenOn();
- }
- // No sleep after the last turn
- if (i < maxTries) {
- SystemClock.sleep(timeoutMs);
- }
- // Exponential back-off.
- timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
- }
- fail("Invalid state for " + mUid + "; expectAvailable=" + expectAvailable + " after "
- + maxTries + " attempts.\nLast error: " + error);
- }
-
- /**
- * Asserts whether the network is blocked by accessing bpf maps if command-line tool supports.
- */
- void assertNetworkAccessBlockedByBpf(boolean expectBlocked, int uid, boolean metered) {
- final String result;
- try {
- result = executeShellCommand(
- "cmd network_stack is-uid-networking-blocked " + uid + " " + metered);
- } catch (AssertionError e) {
- // If NetworkStack is too old to support this command, ignore and continue
- // this test to verify other parts.
- if (e.getMessage().contains("No shell command implementation.")) {
- return;
- }
- throw e;
- }
-
- // Tethering module is too old.
- if (result.contains("API is unsupported")) {
- return;
- }
-
- assertEquals(expectBlocked, parseBooleanOrThrow(result.trim()));
- }
-
- /**
- * Similar to {@link Boolean#parseBoolean} but throws when the input
- * is unexpected instead of returning false.
- */
- private static boolean parseBooleanOrThrow(@NonNull String s) {
- // Don't use Boolean.parseBoolean
- if ("true".equalsIgnoreCase(s)) return true;
- if ("false".equalsIgnoreCase(s)) return false;
- throw new IllegalArgumentException("Unexpected: " + s);
- }
-
- /**
- * Checks whether the network is available as expected.
- *
- * @return error message with the mismatch (or empty if assertion passed).
- */
- private String checkNetworkAccess(boolean expectAvailable,
- @Nullable final String expectedUnavailableError) throws Exception {
- final NetworkCheckResult checkResult = mServiceClient.checkNetworkStatus(mCustomUrl);
- return checkForAvailabilityInNetworkCheckResult(checkResult, expectAvailable,
- expectedUnavailableError);
- }
-
- 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;
- final DetailedState expectedDetailedState;
- if (expectAvailable) {
- expectedState = State.CONNECTED;
- expectedDetailedState = DetailedState.CONNECTED;
- } else {
- expectedState = State.DISCONNECTED;
- expectedDetailedState = DetailedState.BLOCKED;
- }
-
- if (expectAvailable != connected) {
- errors.append(String.format("External site connection failed: expected %s, got %s\n",
- expectAvailable, connected));
- }
- if (expectedState != state || expectedDetailedState != detailedState) {
- errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n",
- expectedState, expectedDetailedState, state, detailedState));
- } else if (!expectAvailable && (expectedUnavailableError != null)
- && !connectionCheckDetails.contains(expectedUnavailableError)) {
- errors.append("Connection unavailable reason mismatch: expected "
- + expectedUnavailableError + "\n");
- }
-
- if (errors.length() > 0) {
- errors.append("\tnetworkInfo: " + networkInfo + "\n");
- errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
- }
- return errors.length() == 0 ? null : errors.toString();
- }
-
- /**
- * Runs a Shell command which is not expected to generate output.
- */
- protected void executeSilentShellCommand(String command) {
- final String result = executeShellCommand(command);
- assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
- }
-
- /**
- * Asserts the result of a command, wait and re-running it a couple times if necessary.
- */
- protected void assertDelayedShellCommand(String command, final String expectedResult)
- throws Exception {
- assertDelayedShellCommand(command, 5, 1, expectedResult);
- }
-
- protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
- final String expectedResult) throws Exception {
- assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() {
-
- @Override
- public boolean isExpected(String result) {
- return expectedResult.equals(result);
- }
-
- @Override
- public String getExpected() {
- return expectedResult;
- }
- });
- }
-
- protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
- ExpectResultChecker checker) throws Exception {
- String result = "";
- for (int i = 1; i <= maxTries; i++) {
- result = executeShellCommand(command).trim();
- if (checker.isExpected(result)) return;
- Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
- + checker.getExpected() + "' on attempt #" + i
- + "; sleeping " + napTimeSeconds + "s before trying again");
- // No sleep after the last turn
- if (i < maxTries) {
- SystemClock.sleep(napTimeSeconds * SECOND_IN_MS);
- }
- }
- fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
- + maxTries
- + " attempts. Last result: '" + result + "'");
- }
-
- protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
- assertRestrictBackgroundWhitelist(uid, true);
- // UID policies live by the Highlander rule: "There can be only one".
- // Hence, if app is whitelisted, it should not be blacklisted.
- assertRestrictBackgroundBlacklist(uid, false);
- }
-
- protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid);
- assertRestrictBackgroundWhitelist(uid, false);
- }
-
- protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
- assertRestrictBackground("restrict-background-whitelist", uid, expected);
- }
-
- protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
- assertRestrictBackgroundBlacklist(uid, true);
- // UID policies live by the Highlander rule: "There can be only one".
- // Hence, if app is blacklisted, it should not be whitelisted.
- assertRestrictBackgroundWhitelist(uid, false);
- }
-
- protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid);
- assertRestrictBackgroundBlacklist(uid, false);
- }
-
- protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception {
- assertRestrictBackground("restrict-background-blacklist", uid, expected);
- }
-
- protected void addAppIdleWhitelist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy add app-idle-whitelist " + uid);
- assertAppIdleWhitelist(uid, true);
- }
-
- protected void removeAppIdleWhitelist(int uid) throws Exception {
- executeShellCommand("cmd netpolicy remove app-idle-whitelist " + uid);
- assertAppIdleWhitelist(uid, false);
- }
-
- protected void assertAppIdleWhitelist(int uid, boolean expected) throws Exception {
- assertRestrictBackground("app-idle-whitelist", uid, expected);
- }
-
- private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
- final int maxTries = 5;
- boolean actual = false;
- final String expectedUid = Integer.toString(uid);
- String uids = "";
- for (int i = 1; i <= maxTries; i++) {
- final String output =
- executeShellCommand("cmd netpolicy list " + list);
- uids = output.split(":")[1];
- for (String candidate : uids.split(" ")) {
- actual = candidate.trim().equals(expectedUid);
- if (expected == actual) {
- return;
- }
- }
- Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
- + expected + ", got " + actual + "); sleeping 1s before polling again");
- // No sleep after the last turn
- if (i < maxTries) {
- SystemClock.sleep(SECOND_IN_MS);
- }
- }
- fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
- + ". Full list: " + uids);
- }
-
- protected void addTempPowerSaveModeWhitelist(String packageName, long duration)
- throws Exception {
- Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist");
- executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName);
- }
-
- protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
- throws Exception {
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName,
- Boolean.toString(expected));
- }
-
- protected void addPowerSaveModeWhitelist(String packageName) throws Exception {
- Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- executeShellCommand("dumpsys deviceidle whitelist +" + packageName);
- assertPowerSaveModeWhitelist(packageName, true);
- }
-
- protected void removePowerSaveModeWhitelist(String packageName) throws Exception {
- Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist");
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- executeShellCommand("dumpsys deviceidle whitelist -" + packageName);
- assertPowerSaveModeWhitelist(packageName, false);
- }
-
- protected void assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected)
- throws Exception {
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- assertDelayedShellCommand("dumpsys deviceidle except-idle-whitelist =" + packageName,
- Boolean.toString(expected));
- }
-
- protected void addPowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
- Log.i(TAG, "Adding package " + packageName + " to power-save-mode-except-idle whitelist");
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- executeShellCommand("dumpsys deviceidle except-idle-whitelist +" + packageName);
- assertPowerSaveModeExceptIdleWhitelist(packageName, true);
- }
-
- protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
- Log.i(TAG, "Removing package " + packageName
- + " from power-save-mode-except-idle whitelist");
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- executeShellCommand("dumpsys deviceidle except-idle-whitelist reset");
- assertPowerSaveModeExceptIdleWhitelist(packageName, false);
- }
-
- protected void turnBatteryOn() throws Exception {
- executeSilentShellCommand("cmd battery unplug");
- executeSilentShellCommand("cmd battery set status "
- + BatteryManager.BATTERY_STATUS_DISCHARGING);
- assertBatteryState(false);
- }
-
- protected void turnBatteryOff() throws Exception {
- executeSilentShellCommand("cmd battery set ac " + BATTERY_PLUGGED_ANY);
- executeSilentShellCommand("cmd battery set level 100");
- executeSilentShellCommand("cmd battery set status "
- + BatteryManager.BATTERY_STATUS_CHARGING);
- 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) {
- Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS);
- }
- if (isDevicePluggedIn() != pluggedIn) {
- fail("Timed out waiting for the plugged-in state to change,"
- + " expected pluggedIn: " + pluggedIn);
- }
- }
-
- private boolean isDevicePluggedIn() {
- final Intent batteryIntent = mContext.registerReceiver(null, BATTERY_CHANGED_FILTER);
- return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0;
- }
-
- protected void turnScreenOff() throws Exception {
- if (!mLock.isHeld()) mLock.acquire();
- executeSilentShellCommand("input keyevent KEYCODE_SLEEP");
- }
-
- protected void turnScreenOn() throws Exception {
- executeSilentShellCommand("input keyevent KEYCODE_WAKEUP");
- if (mLock.isHeld()) mLock.release();
- executeSilentShellCommand("wm dismiss-keyguard");
- }
-
- protected void setBatterySaverMode(boolean enabled) throws Exception {
- if (!isBatterySaverSupported()) {
- return;
- }
- Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
- if (enabled) {
- turnBatteryOn();
- AmUtils.waitForBroadcastBarrier();
- executeSilentShellCommand("cmd power set-mode 1");
- } else {
- executeSilentShellCommand("cmd power set-mode 0");
- turnBatteryOff();
- AmUtils.waitForBroadcastBarrier();
- }
- }
-
- protected void setDozeMode(boolean enabled) throws Exception {
- if (!isDozeModeSupported()) {
- return;
- }
-
- Log.i(TAG, "Setting Doze Mode to " + enabled);
- if (enabled) {
- turnBatteryOn();
- turnScreenOff();
- executeShellCommand("dumpsys deviceidle force-idle deep");
- } else {
- turnScreenOn();
- turnBatteryOff();
- executeShellCommand("dumpsys deviceidle unforce");
- }
- assertDozeMode(enabled);
- }
-
- protected void assertDozeMode(boolean enabled) throws Exception {
- assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
- }
-
- protected void stopApp() {
- executeSilentShellCommand("am stop-app " + TEST_APP2_PKG);
- }
-
- protected void setAppIdle(boolean isIdle) throws Exception {
- setAppIdleNoAssert(isIdle);
- assertAppIdle(isIdle);
- }
-
- protected void setAppIdleNoAssert(boolean isIdle) throws Exception {
- if (!isAppStandbySupported()) {
- return;
- }
- Log.i(TAG, "Setting app idle to " + isIdle);
- final String bucketName = isIdle ? "rare" : "active";
- executeSilentShellCommand("am set-standby-bucket " + TEST_APP2_PKG + " " + bucketName);
- }
-
- protected void assertAppIdle(boolean isIdle) throws Exception {
- try {
- assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG,
- 30 /* maxTries */, 1 /* napTimeSeconds */, "Idle=" + isIdle);
- } catch (Throwable e) {
- throw e;
- }
- }
-
- /**
- * Starts a service that will register a broadcast receiver to receive
- * {@code RESTRICT_BACKGROUND_CHANGE} intents.
- * <p>
- * The service must run in a separate app because otherwise it would be killed every time
- * {@link #runDeviceTests(String, String)} is executed.
- */
- protected void registerBroadcastReceiver() throws Exception {
- mServiceClient.registerBroadcastReceiver();
-
- final Intent intent = new Intent(ACTION_RECEIVER_READY)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- // Wait until receiver is ready.
- final int maxTries = 10;
- for (int i = 1; i <= maxTries; i++) {
- final String message = sendOrderedBroadcast(intent, SECOND_IN_MS * 4);
- Log.d(TAG, "app2 receiver acked: " + message);
- if (message != null) {
- return;
- }
- Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again");
- // No sleep after the last turn
- if (i < maxTries) {
- SystemClock.sleep(SECOND_IN_MS);
- }
- }
- fail("app2 receiver is not ready in " + mUid);
- }
-
- protected void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)
- throws Exception {
- Log.i(TAG, "Registering network callback for request: " + request);
- mServiceClient.registerNetworkCallback(request, cb);
- }
-
- protected void unregisterNetworkCallback() throws Exception {
- mServiceClient.unregisterNetworkCallback();
- }
-
- /**
- * Registers a {@link NotificationListenerService} implementation that will execute the
- * notification actions right after the notification is sent.
- */
- protected void registerNotificationListenerService() throws Exception {
- executeShellCommand("cmd notification allow_listener "
- + MyNotificationListenerService.getId());
- final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
- final ComponentName listenerComponent = MyNotificationListenerService.getComponentName();
- assertTrue(listenerComponent + " has not been granted access",
- nm.isNotificationListenerAccessGranted(listenerComponent));
- }
-
- protected void setPendingIntentAllowlistDuration(long durationMs) {
- mDeviceIdleDeviceConfigStateHelper.set("notification_allowlist_duration_ms",
- String.valueOf(durationMs));
- }
-
- protected void resetDeviceIdleSettings() {
- mDeviceIdleDeviceConfigStateHelper.restoreOriginalValues();
- }
-
- protected void launchActivity() throws Exception {
- turnScreenOn();
- final CountDownLatch latch = new CountDownLatch(1);
- final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_ACTIVTIY);
- final RemoteCallback callback = new RemoteCallback(result -> latch.countDown());
- launchIntent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
- mContext.startActivity(launchIntent);
- // There might be a race when app2 is launched but ACTION_FINISH_ACTIVITY has not registered
- // before test calls finishActivity(). When the issue is happened, there is no way to fix
- // it, so have a callback design to make sure that the app is launched completely and
- // ACTION_FINISH_ACTIVITY will be registered before leaving this method.
- if (!latch.await(LAUNCH_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- fail("Timed out waiting for launching activity");
- }
- }
-
- 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();
- } else if (type == TYPE_COMPONENT_ACTIVTIY) {
- turnScreenOn();
- final CountDownLatch latch = new CountDownLatch(1);
- final Intent launchIntent = getIntentForComponent(type);
- final Bundle extras = new Bundle();
- 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().first;
- final NetworkCheckResult networkCheckResult = result.get().second;
- if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
- 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, networkCheckResult.details);
- // App didn't come to foreground when the activity is started, so try again.
- assertTopNetworkAccess(true);
- } else {
- fail("Unexpected resultCode=" + resultCode
- + "; networkCheckResult=[" + networkCheckResult + "]");
- }
- } else {
- 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 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)
- .setTransientExtras(extras)
- .build();
- assertEquals("Error scheduling " + jobInfo,
- 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().first;
- final NetworkCheckResult networkCheckResult = result.get().second;
- if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
- 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
- // doesn't have network access immediately after starting, so check again.
- assertNetworkAccess(expectAvailable, false /* needScreenOn */);
- }
- } else {
- fail("Unexpected resultCode=" + resultCode
- + "; networkCheckResult=[" + networkCheckResult + "]");
- }
- } else {
- fail("Timed out waiting for network availability status from app2's expedited job ("
- + mUid + ")");
- }
- } else {
- throw new IllegalArgumentException("Unknown type: " + type);
- }
- }
-
- protected void startActivity() throws Exception {
- final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_ACTIVTIY);
- mContext.startActivity(launchIntent);
- }
-
- private void startForegroundService() throws Exception {
- final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE);
- mContext.startForegroundService(launchIntent);
- assertForegroundServiceState();
- }
-
- private Intent getIntentForComponent(int type) {
- final Intent intent = new Intent();
- if (type == TYPE_COMPONENT_ACTIVTIY) {
- intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
- intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
- .setFlags(1);
- } else {
- fail("Unknown type: " + type);
- }
- return intent;
- }
-
- protected void stopForegroundService() throws Exception {
- executeShellCommand(String.format("am startservice -f 2 %s/%s",
- TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS));
- // NOTE: cannot assert state because it depends on whether activity was on top before.
- }
-
- private Binder getNewNetworkStateObserver(final CountDownLatch latch,
- final AtomicReference<Pair<Integer, NetworkCheckResult>> result) {
- return new INetworkStateObserver.Stub() {
- @Override
- public void onNetworkStateChecked(int resultCode,
- NetworkCheckResult networkCheckResult) {
- result.set(Pair.create(resultCode, networkCheckResult));
- latch.countDown();
- }
- };
- }
-
- /**
- * Finishes an activity on app2 so its process is demoted from foreground status.
- */
- protected void finishActivity() throws Exception {
- 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 {
- Log.d(TAG, "Sending notification broadcast (id=" + notificationId
- + ", type=" + notificationType);
- mServiceClient.sendNotification(notificationId, notificationType);
- }
-
- protected String showToast() {
- final Intent intent = new Intent(ACTION_SHOW_TOAST);
- intent.setPackage(TEST_APP2_PKG);
- Log.d(TAG, "Sending request to show toast");
- try {
- return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS);
- } catch (Exception e) {
- return "";
- }
- }
-
- private ProcessState getProcessStateByUid(int uid) throws Exception {
- return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
- }
-
- private static class ProcessState {
- private final String fullState;
- final int state;
-
- ProcessState(String fullState) {
- this.fullState = fullState;
- try {
- this.state = Integer.parseInt(fullState.split(" ")[0]);
- } catch (Exception e) {
- throw new IllegalArgumentException("Could not parse " + fullState);
- }
- }
-
- @Override
- public String toString() {
- return fullState;
- }
- }
-
- /**
- * Helper class used to assert the result of a Shell command.
- */
- protected static interface ExpectResultChecker {
- /**
- * Checkes whether the result of the command matched the expectation.
- */
- boolean isExpected(String result);
- /**
- * Gets the expected result so it's displayed on log and failure messages.
- */
- String getExpected();
- }
-
- protected void setRestrictedNetworkingMode(boolean enabled) throws Exception {
- executeSilentShellCommand(
- "settings put global restricted_networking_mode " + (enabled ? 1 : 0));
- assertRestrictedNetworkingModeState(enabled);
- }
-
- protected void assertRestrictedNetworkingModeState(boolean enabled) throws Exception {
- assertDelayedShellCommand("cmd netpolicy get restricted-mode",
- "Restricted mode status: " + (enabled ? "enabled" : "disabled"));
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
deleted file mode 100644
index 6b802f6..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-
-@RequiredProperties({METERED_NETWORK})
-public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
deleted file mode 100644
index 2e725ae..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
deleted file mode 100644
index 2e421f6..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-
-@RequiredProperties({METERED_NETWORK})
-public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
deleted file mode 100644
index 0be5644..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
deleted file mode 100644
index bfccce9..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getUiDevice;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-import static org.junit.Assume.assumeTrue;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.compatibility.common.util.ThrowingRunnable;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTestCase {
- private static final int TEST_ITERATION_COUNT = 5;
-
- @Before
- public final void setUp() throws Exception {
- super.setUp();
- resetDeviceState();
- }
-
- @After
- public final void tearDown() throws Exception {
- super.tearDown();
- stopApp();
- resetDeviceState();
- }
-
- private void resetDeviceState() throws Exception {
- resetBatteryState();
- setBatterySaverMode(false);
- setRestrictBackground(false);
- setAppIdle(false);
- setDozeMode(false);
- }
-
-
- @Test
- @RequiredProperties({BATTERY_SAVER_MODE})
- public void testStartActivity_batterySaver() throws Exception {
- setBatterySaverMode(true);
- assertNetworkAccess(false, null);
- assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver", null);
- }
-
- @Test
- @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
- public void testStartActivity_dataSaver() throws Exception {
- setRestrictBackground(true);
- assertNetworkAccess(false, null);
- assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver", null);
- }
-
- @Test
- @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);
- }
-
- @Test
- @RequiredProperties({APP_STANDBY_MODE})
- 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);
- }
-
- @Test
- public void testStartActivity_default() throws Exception {
- assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
- assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(mProcessStateTransitionLongDelayMs);
- assertNetworkAccess(false, null);
- });
- }
-
- private void assertLaunchedActivityHasNetworkAccess(String testName,
- ThrowingRunnable onBeginIteration) throws Exception {
- for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
- if (onBeginIteration != null) {
- onBeginIteration.run();
- }
- Log.i(TAG, testName + " start #" + i);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- getUiDevice().pressHome();
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- Log.i(TAG, testName + " end #" + i);
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
deleted file mode 100644
index 66e0d00..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
-
-import static com.android.compatibility.common.util.FeatureUtil.isTV;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-import static com.android.cts.netpolicy.hostside.Property.NO_DATA_SAVER_MODE;
-
-import static org.junit.Assert.fail;
-
-import androidx.test.filters.LargeTest;
-
-import com.android.compatibility.common.util.CddTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
-@LargeTest
-public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
-
- private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
- "com.android.providers.downloads"
- };
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- // Set initial state.
- setRestrictBackground(false);
- removeRestrictBackgroundWhitelist(mUid);
- removeRestrictBackgroundBlacklist(mUid);
-
- registerBroadcastReceiver();
- assertRestrictBackgroundChangedReceived(0);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
-
- setRestrictBackground(false);
- }
-
- @Test
- public void testGetRestrictBackgroundStatus_disabled() throws Exception {
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
-
- // Verify status is always disabled, never whitelisted
- addRestrictBackgroundWhitelist(mUid);
- assertRestrictBackgroundChangedReceived(0);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
- }
-
- @Test
- public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
- setRestrictBackground(true);
- assertRestrictBackgroundChangedReceived(1);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- addRestrictBackgroundWhitelist(mUid);
- assertRestrictBackgroundChangedReceived(2);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
-
- removeRestrictBackgroundWhitelist(mUid);
- assertRestrictBackgroundChangedReceived(3);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
- }
-
- @Test
- public void testGetRestrictBackgroundStatus_enabled() throws Exception {
- setRestrictBackground(true);
- assertRestrictBackgroundChangedReceived(1);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- // Make sure foreground app doesn't lose access upon enabling Data Saver.
- setRestrictBackground(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- setRestrictBackground(true);
- assertTopNetworkAccess(true);
-
- // Although it should not have access while the screen is off.
- turnScreenOff();
- assertBackgroundNetworkAccess(false);
- turnScreenOn();
- // On some TVs, it is possible that the activity on top may change after the screen is
- // turned off and on again, so relaunch the activity in the test app again.
- if (isTV()) {
- startActivity();
- }
- assertTopNetworkAccess(true);
-
- // Goes back to background state.
- finishActivity();
- assertBackgroundNetworkAccess(false);
-
- // Make sure foreground service doesn't lose access upon enabling Data Saver.
- setRestrictBackground(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
- setRestrictBackground(true);
- assertForegroundServiceNetworkAccess();
- stopForegroundService();
- assertBackgroundNetworkAccess(false);
- }
-
- @Test
- public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
- addRestrictBackgroundBlacklist(mUid);
- assertRestrictBackgroundChangedReceived(1);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- assertsForegroundAlwaysHasNetworkAccess();
- assertRestrictBackgroundChangedReceived(1);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
-
- // UID policies live by the Highlander rule: "There can be only one".
- // Hence, if app is whitelisted, it should not be blacklisted anymore.
- setRestrictBackground(true);
- assertRestrictBackgroundChangedReceived(2);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
- addRestrictBackgroundWhitelist(mUid);
- assertRestrictBackgroundChangedReceived(3);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
-
- // Check status after removing blacklist.
- // ...re-enables first
- addRestrictBackgroundBlacklist(mUid);
- assertRestrictBackgroundChangedReceived(4);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
- assertsForegroundAlwaysHasNetworkAccess();
- // ... remove blacklist - access's still rejected because Data Saver is on
- removeRestrictBackgroundBlacklist(mUid);
- assertRestrictBackgroundChangedReceived(4);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
- assertsForegroundAlwaysHasNetworkAccess();
- // ... finally, disable Data Saver
- setRestrictBackground(false);
- assertRestrictBackgroundChangedReceived(5);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
- assertsForegroundAlwaysHasNetworkAccess();
- }
-
- @Test
- public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
- final StringBuilder error = new StringBuilder();
- for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
- int uid = -1;
- try {
- uid = getUid(packageName);
- assertRestrictBackgroundWhitelist(uid, true);
- } catch (Throwable t) {
- error.append("\nFailed for '").append(packageName).append("'");
- if (uid > 0) {
- error.append(" (uid ").append(uid).append(")");
- }
- error.append(": ").append(t).append("\n");
- }
- }
- if (error.length() > 0) {
- fail(error.toString());
- }
- }
-
- @RequiredProperties({NO_DATA_SAVER_MODE})
- @CddTest(requirement="7.4.7/C-2-2")
- @Test
- public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
- setRestrictBackground(true);
- assertRestrictBackgroundChangedReceived(0);
-
- setRestrictBackground(false);
- assertRestrictBackgroundChangedReceived(0);
-
- setRestrictBackground(true);
- assertRestrictBackgroundChangedReceived(0);
- }
-
- private void assertDataSaverStatusOnBackground(int expectedStatus) throws Exception {
- assertRestrictBackgroundStatus(expectedStatus);
- assertBackgroundNetworkAccess(expectedStatus != RESTRICT_BACKGROUND_STATUS_ENABLED);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
deleted file mode 100644
index 69ca206..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
-
-import android.content.pm.PackageManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.Direction;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.UiAutomatorUtils2;
-
-import org.junit.After;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Period;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.List;
-
-public class DataWarningReceiverTest extends AbstractRestrictBackgroundNetworkTestCase {
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- clearSnoozeTimestamps();
- registerBroadcastReceiver();
- turnScreenOn();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @Test
- public void testSnoozeWarningNotReceived() throws Exception {
- Assume.assumeTrue("Feature not supported: " + PackageManager.FEATURE_TELEPHONY,
- mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
- final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
- final int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- Assume.assumeTrue("Valid subId not found",
- subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-
- setSubPlanOwner(subId, TEST_PKG);
- final List<SubscriptionPlan> originalPlans = sm.getSubscriptionPlans(subId);
- try {
- // In NetworkPolicyManagerService class, we set the data warning bytes to 90% of
- // data limit bytes. So, create the subscription plan in such a way this data warning
- // threshold is already reached.
- final SubscriptionPlan plan = SubscriptionPlan.Builder
- .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
- Period.ofMonths(1))
- .setTitle("CTS")
- .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
- .setDataUsage(999_000_000, System.currentTimeMillis())
- .build();
- sm.setSubscriptionPlans(subId, Arrays.asList(plan));
- final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
- uiDevice.openNotification();
- try {
- final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(
- By.text("Data warning"));
- Assume.assumeNotNull(uiObject);
- uiObject.wait(Until.clickable(true), 10_000L);
- uiObject.getParent().swipe(Direction.RIGHT, 1.0f);
- } catch (Throwable t) {
- Assume.assumeNoException(
- "Error occurred while finding and swiping the notification", t);
- }
- assertSnoozeWarningNotReceived();
- uiDevice.pressHome();
- } finally {
- sm.setSubscriptionPlans(subId, originalPlans);
- setSubPlanOwner(subId, "");
- }
- }
-
- private static void setSubPlanOwner(int subId, String packageName) throws Exception {
- SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
- "cmd netpolicy set sub-plan-owner " + subId + " " + packageName);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
deleted file mode 100644
index 810fd19..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-
-@RequiredProperties({METERED_NETWORK})
-public class DefaultRestrictionsMeteredTest extends AbstractDefaultRestrictionsTest {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
deleted file mode 100644
index fef546c..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class DefaultRestrictionsNonMeteredTest extends AbstractDefaultRestrictionsTest {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
deleted file mode 100644
index 741dd7e..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-
-@RequiredProperties({METERED_NETWORK})
-public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
deleted file mode 100644
index f343df5..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
deleted file mode 100644
index 2dc6cc4..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
-import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
-import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
-
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.compatibility.common.util.OnFailureRule;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-public class DumpOnFailureRule extends OnFailureRule {
- private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
- "CtsHostsideNetworkPolicyTests");
-
- @Override
- public void onTestFailure(Statement base, Description description, Throwable throwable) {
- if (throwable instanceof AssumptionViolatedException) {
- final String testName = description.getClassName() + "_" + description.getMethodName();
- Log.d(TAG, "Skipping test " + testName + ": " + throwable);
- return;
- }
-
- prepareDumpRootDir();
- final String shortenedTestName = getShortenedTestName(description);
- final File dumpFile = new File(mDumpDir, "dump-" + shortenedTestName);
- Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
- try (FileOutputStream out = new FileOutputStream(dumpFile)) {
- for (String cmd : new String[] {
- "dumpsys netpolicy",
- "dumpsys network_management",
- "dumpsys usagestats " + TEST_PKG + " " + TEST_APP2_PKG,
- "dumpsys usagestats appstandby",
- "dumpsys connectivity trafficcontroller",
- "dumpsys netd trafficcontroller",
- "dumpsys platform_compat", // TODO (b/279829773): Remove this dump
- "dumpsys jobscheduler " + TEST_APP2_PKG, // TODO (b/288220398): Remove this dump
- }) {
- dumpCommandOutput(out, cmd);
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Error opening file: " + dumpFile, e);
- } catch (IOException e) {
- Log.e(TAG, "Error closing file: " + dumpFile, e);
- }
- final UiDevice uiDevice = UiDevice.getInstance(
- InstrumentationRegistry.getInstrumentation());
- final File screenshotFile = new File(mDumpDir, "sc-" + shortenedTestName + ".png");
- uiDevice.takeScreenshot(screenshotFile);
- final File windowHierarchyFile = new File(mDumpDir, "wh-" + shortenedTestName + ".xml");
- try {
- uiDevice.dumpWindowHierarchy(windowHierarchyFile);
- } catch (IOException e) {
- Log.e(TAG, "Error dumping window hierarchy", e);
- }
- }
-
- private String getShortenedTestName(Description description) {
- final String qualifiedClassName = description.getClassName();
- final String className = qualifiedClassName.substring(
- qualifiedClassName.lastIndexOf(".") + 1);
- final String shortenedClassName = className.chars()
- .filter(Character::isUpperCase)
- .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
- .toString();
- return shortenedClassName + "_" + description.getMethodName();
- }
-
- void dumpCommandOutput(FileOutputStream out, String cmd) {
- final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
- .getUiAutomation().executeShellCommand(cmd);
- try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
- FileUtils.copy(in, out);
- out.write("\n\n=================================================================\n\n"
- .getBytes(StandardCharsets.UTF_8));
- } catch (IOException e) {
- Log.e(TAG, "Error dumping '" + cmd + "'", e);
- }
- }
-
- void prepareDumpRootDir() {
- if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
- Log.e(TAG, "Error creating " + mDumpDir);
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
deleted file mode 100644
index d56a50b..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-
-@RequiredProperties({METERED_NETWORK})
-public class ExpeditedJobMeteredTest extends AbstractExpeditedJobTest {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
deleted file mode 100644
index 0a776ee..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-@RequiredProperties({NON_METERED_NETWORK})
-public class ExpeditedJobNonMeteredTest extends AbstractExpeditedJobTest {
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
deleted file mode 100644
index 4f4e68e..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setupActiveNetworkMeteredness;
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-import android.util.ArraySet;
-
-import com.android.compatibility.common.util.BeforeAfterRule;
-import com.android.compatibility.common.util.ThrowingRunnable;
-
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-public class MeterednessConfigurationRule extends BeforeAfterRule {
- private ThrowingRunnable mMeterednessResetter;
-
- @Override
- public void onBefore(Statement base, Description description) throws Throwable {
- final ArraySet<Property> requiredProperties
- = RequiredPropertiesRule.getRequiredProperties();
- if (requiredProperties.contains(METERED_NETWORK)) {
- configureNetworkMeteredness(true);
- } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
- configureNetworkMeteredness(false);
- }
- }
-
- @Override
- public void onAfter(Statement base, Description description) throws Throwable {
- resetNetworkMeteredness();
- }
-
- public void configureNetworkMeteredness(boolean metered) throws Exception {
- mMeterednessResetter = setupActiveNetworkMeteredness(metered);
- }
-
- public void resetNetworkMeteredness() throws Exception {
- if (mMeterednessResetter != null) {
- mMeterednessResetter.run();
- mMeterednessResetter = null;
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
deleted file mode 100644
index b0fa106..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
-import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
-import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
- * and Data Saver Mode) are applied simultaneously.
- * <p>
- * <strong>NOTE: </strong>it might sound like the test methods on this class are testing too much,
- * which would make it harder to diagnose individual failures, but the assumption is that such
- * failure most likely will happen when the restriction is tested individually as well.
- */
-public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
- private static final String TAG = "MixedModesTest";
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- // Set initial state.
- removeRestrictBackgroundWhitelist(mUid);
- removeRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
-
- registerBroadcastReceiver();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
-
- try {
- setRestrictBackground(false);
- } finally {
- setBatterySaverMode(false);
- }
- }
-
- /**
- * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
- */
- @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
- @Test
- public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(true);
- try {
- setRestrictBackground(true);
- setBatterySaverMode(true);
-
- Log.v(TAG, "Not whitelisted for any.");
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
- addRestrictBackgroundWhitelist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
- Log.v(TAG, "Whitelisted for both.");
- addRestrictBackgroundWhitelist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundBlacklist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- } finally {
- meterednessConfiguration.resetNetworkMeteredness();
- }
- }
-
- /**
- * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
- * networks.
- */
- @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
- @Test
- public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(false);
- try {
- setRestrictBackground(true);
- setBatterySaverMode(true);
-
- Log.v(TAG, "Not whitelisted for any.");
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
- addRestrictBackgroundWhitelist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
- Log.v(TAG, "Whitelisted for both.");
- addRestrictBackgroundWhitelist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundBlacklist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removeRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- } finally {
- meterednessConfiguration.resetNetworkMeteredness();
- }
- }
-
- /**
- * Tests that powersave whitelists works as expected when doze and battery saver modes
- * are enabled.
- */
- @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
- @Test
- public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- setBatterySaverMode(true);
- setDozeMode(true);
-
- try {
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- } finally {
- setBatterySaverMode(false);
- setDozeMode(false);
- }
- }
-
- /**
- * Tests that powersave whitelists works as expected when doze and appIdle modes
- * are enabled.
- */
- @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
- @Test
- public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
- setDozeMode(true);
- setAppIdle(true);
-
- try {
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
-
- removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setDozeMode(false);
- }
- }
-
- @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
- @Test
- public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
- setDozeMode(true);
- setAppIdle(true);
-
- try {
- assertBackgroundNetworkAccess(false);
-
- addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(true);
-
- // Wait until the whitelist duration is expired.
- SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setDozeMode(false);
- }
- }
-
- @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
- @Test
- public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
- setBatterySaverMode(true);
- setAppIdle(true);
-
- try {
- assertBackgroundNetworkAccess(false);
-
- addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(true);
-
- // Wait until the whitelist duration is expired.
- SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setBatterySaverMode(false);
- }
- }
-
- /**
- * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
- */
- @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
- @Test
- public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
- setDozeMode(true);
- setAppIdle(true);
-
- try {
- assertBackgroundNetworkAccess(false);
-
- // UID still shouldn't have access because of Doze.
- addAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
-
- removeAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setDozeMode(false);
- }
- }
-
- @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
- @Test
- public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- setDozeMode(true);
- setAppIdle(true);
-
- try {
- assertBackgroundNetworkAccess(false);
-
- addAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
-
- addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(true);
-
- // Wait until the whitelist duration is expired.
- SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setDozeMode(false);
- removeAppIdleWhitelist(mUid);
- }
- }
-
- @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
- @Test
- public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- setBatterySaverMode(true);
- setAppIdle(true);
-
- try {
- assertBackgroundNetworkAccess(false);
-
- addAppIdleWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
-
- addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(true);
-
- // Wait until the whitelist duration is expired.
- SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
- assertBackgroundNetworkAccess(false);
- } finally {
- setAppIdle(false);
- setBatterySaverMode(false);
- removeAppIdleWhitelist(mUid);
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
deleted file mode 100644
index 6dc9921..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteInput;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-/**
- * NotificationListenerService implementation that executes the notification actions once they're
- * created.
- */
-public class MyNotificationListenerService extends NotificationListenerService {
- private static final String TAG = "MyNotificationListenerService";
-
- @Override
- public void onListenerConnected() {
- Log.d(TAG, "onListenerConnected()");
- }
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn) {
- Log.d(TAG, "onNotificationPosted(): " + sbn);
- if (!sbn.getPackageName().startsWith(getPackageName())) {
- Log.v(TAG, "ignoring notification from a different package");
- return;
- }
- final PendingIntentSender sender = new PendingIntentSender();
- final Notification notification = sbn.getNotification();
- if (notification.contentIntent != null) {
- sender.send("content", notification.contentIntent);
- }
- if (notification.deleteIntent != null) {
- sender.send("delete", notification.deleteIntent);
- }
- if (notification.fullScreenIntent != null) {
- sender.send("full screen", notification.fullScreenIntent);
- }
- if (notification.actions != null) {
- for (Notification.Action action : notification.actions) {
- sender.send("action", action.actionIntent);
- sender.send("action extras", action.getExtras());
- final RemoteInput[] remoteInputs = action.getRemoteInputs();
- if (remoteInputs != null && remoteInputs.length > 0) {
- for (RemoteInput remoteInput : remoteInputs) {
- sender.send("remote input extras", remoteInput.getExtras());
- }
- }
- }
- }
- sender.send("notification extras", notification.extras);
- }
-
- static String getId() {
- return String.format("%s/%s", MyNotificationListenerService.class.getPackage().getName(),
- MyNotificationListenerService.class.getName());
- }
-
- static ComponentName getComponentName() {
- return new ComponentName(MyNotificationListenerService.class.getPackage().getName(),
- MyNotificationListenerService.class.getName());
- }
-
- private static final class PendingIntentSender {
- private PendingIntent mSentIntent = null;
- private String mReason = null;
-
- private void send(String reason, PendingIntent pendingIntent) {
- if (pendingIntent == null) {
- // Could happen on action that only has extras
- Log.v(TAG, "Not sending null pending intent for " + reason);
- return;
- }
- if (mSentIntent != null || mReason != null) {
- // Sanity check: make sure test case set up just one pending intent in the
- // notification, otherwise it could pass because another pending intent caused the
- // whitelisting.
- throw new IllegalStateException("Already sent a PendingIntent (" + mSentIntent
- + ") for reason '" + mReason + "' when requested another for '" + reason
- + "' (" + pendingIntent + ")");
- }
- Log.i(TAG, "Sending pending intent for " + reason + ":" + pendingIntent);
- try {
- pendingIntent.send();
- mSentIntent = pendingIntent;
- mReason = reason;
- } catch (CanceledException e) {
- Log.w(TAG, "Pending intent " + pendingIntent + " canceled");
- }
- }
-
- private void send(String reason, Bundle extras) {
- if (extras != null) {
- for (String key : extras.keySet()) {
- Object value = extras.get(key);
- if (value instanceof PendingIntent) {
- send(reason + " with key '" + key + "'", (PendingIntent) value);
- }
- }
- }
- }
-
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
deleted file mode 100644
index 71b28f6..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import android.app.job.JobInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.net.NetworkRequest;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-public class MyServiceClient {
- private static final int TIMEOUT_MS = 20_000;
- private static final String PACKAGE = MyServiceClient.class.getPackage().getName();
- private static final String APP2_PACKAGE = PACKAGE + ".app2";
- private static final String SERVICE_NAME = APP2_PACKAGE + ".MyService";
-
- private Context mContext;
- private ServiceConnection mServiceConnection;
- private volatile IMyService mService;
- private final ConditionVariable mServiceCondition = new ConditionVariable();
-
- public MyServiceClient(Context context) {
- mContext = context;
- }
-
- /**
- * Binds to a service in the test app to communicate state.
- * @param bindPriorityFlags Flags to influence the process-state of the bound app.
- */
- public void bind(int bindPriorityFlags) {
- if (mService != null) {
- throw new IllegalStateException("Already bound");
- }
- mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IMyService.Stub.asInterface(service);
- mServiceCondition.open();
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mServiceCondition.close();
- mService = null;
- }
- };
-
- final Intent intent = new Intent();
- intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
- // Needs to use BIND_NOT_FOREGROUND so app2 does not run in
- // the same process state as app
- mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
- | bindPriorityFlags);
- ensureServiceConnection();
- }
-
- public void unbind() {
- if (mService != null) {
- mContext.unbindService(mServiceConnection);
- }
- }
-
- private void ensureServiceConnection() {
- if (mService != null) {
- return;
- }
- mServiceCondition.block(TIMEOUT_MS);
- if (mService == null) {
- throw new IllegalStateException(
- "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
- }
- }
-
- public void registerBroadcastReceiver() throws RemoteException {
- ensureServiceConnection();
- mService.registerBroadcastReceiver();
- }
-
- public int getCounters(String receiverName, String action) throws RemoteException {
- ensureServiceConnection();
- return mService.getCounters(receiverName, action);
- }
-
- /** Retrieves the network state as observed from the bound test app */
- public NetworkCheckResult checkNetworkStatus(String address) throws RemoteException {
- ensureServiceConnection();
- return mService.checkNetworkStatus(address);
- }
-
- public String getRestrictBackgroundStatus() throws RemoteException {
- ensureServiceConnection();
- return mService.getRestrictBackgroundStatus();
- }
-
- public void sendNotification(int notificationId, String notificationType)
- throws RemoteException {
- ensureServiceConnection();
- mService.sendNotification(notificationId, notificationType);
- }
-
- public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)
- throws RemoteException {
- ensureServiceConnection();
- mService.registerNetworkCallback(request, cb);
- }
-
- public void unregisterNetworkCallback() throws RemoteException {
- ensureServiceConnection();
- mService.unregisterNetworkCallback();
- }
-
- public int scheduleJob(JobInfo jobInfo) throws RemoteException {
- ensureServiceConnection();
- return mService.scheduleJob(jobInfo);
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
deleted file mode 100644
index 3934cfa..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.cts.util.CtsNetUtils;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Objects;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
- private Network mNetwork;
- private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
- private CtsNetUtils mCtsNetUtils;
- private static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
-
- @Rule
- public final MeterednessConfigurationRule mMeterednessConfiguration
- = new MeterednessConfigurationRule();
-
- enum CallbackState {
- NONE,
- AVAILABLE,
- LOST,
- BLOCKED_STATUS,
- CAPABILITIES
- }
-
- private static class CallbackInfo {
- public final CallbackState state;
- public final Network network;
- public final Object arg;
-
- CallbackInfo(CallbackState s, Network n, Object o) {
- state = s; network = n; arg = o;
- }
-
- public String toString() {
- return String.format("%s (%s) (%s)", state, network, arg);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof CallbackInfo)) return false;
- // Ignore timeMs, since it's unpredictable.
- final CallbackInfo other = (CallbackInfo) o;
- return (state == other.state) && Objects.equals(network, other.network)
- && Objects.equals(arg, other.arg);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(state, network, arg);
- }
- }
-
- private class TestNetworkCallback extends INetworkCallback.Stub {
- private static final int TEST_CONNECT_TIMEOUT_MS = 30_000;
- private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
-
- private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
-
- protected void setLastCallback(CallbackState state, Network network, Object o) {
- mCallbacks.offer(new CallbackInfo(state, network, o));
- }
-
- CallbackInfo nextCallback(int timeoutMs) {
- CallbackInfo cb = null;
- try {
- cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- }
- if (cb == null) {
- fail("Did not receive callback after " + timeoutMs + "ms");
- }
- return cb;
- }
-
- CallbackInfo expectCallback(CallbackState state, Network expectedNetwork, Object o) {
- final CallbackInfo expected = new CallbackInfo(state, expectedNetwork, o);
- final CallbackInfo actual = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
- assertEquals("Unexpected callback:", expected, actual);
- return actual;
- }
-
- @Override
- public void onAvailable(Network network) {
- setLastCallback(CallbackState.AVAILABLE, network, null);
- }
-
- @Override
- public void onLost(Network network) {
- setLastCallback(CallbackState.LOST, network, null);
- }
-
- @Override
- public void onBlockedStatusChanged(Network network, boolean blocked) {
- setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
- }
-
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
- setLastCallback(CallbackState.CAPABILITIES, network, cap);
- }
-
- public Network expectAvailableCallbackAndGetNetwork() {
- final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS);
- if (cb.state != CallbackState.AVAILABLE) {
- fail("Network is not available. Instead obtained the following callback :" + cb);
- }
- return cb.network;
- }
-
- public void drainAndWaitForIdle() {
- try {
- do {
- mCallbacks.drainTo(new ArrayList<>());
- } while (mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS) != null);
- } catch (InterruptedException ie) {
- Log.e(TAG, "Interrupted while draining callback queue", ie);
- Thread.currentThread().interrupt();
- }
- }
-
- public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
- expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked);
- }
-
- public void expectBlockedStatusCallbackEventually(Network expectedNetwork,
- boolean expectBlocked) {
- final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
- do {
- final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
- if (cb.state == CallbackState.BLOCKED_STATUS
- && cb.network.equals(expectedNetwork)) {
- assertEquals(expectBlocked, cb.arg);
- return;
- }
- } while (System.currentTimeMillis() <= deadline);
- fail("Didn't receive onBlockedStatusChanged()");
- }
-
- public void expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap,
- int cap) {
- final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
- do {
- final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
- if (cb.state != CallbackState.CAPABILITIES
- || !expectedNetwork.equals(cb.network)
- || (hasCap != ((NetworkCapabilities) cb.arg).hasCapability(cap))) {
- Log.i("NetworkCallbackTest#expectCapabilitiesCallback",
- "Ignoring non-matching callback : " + cb);
- continue;
- }
- // Found a match, return
- return;
- } while (System.currentTimeMillis() <= deadline);
- fail("Didn't receive the expected callback to onCapabilitiesChanged(). Check the "
- + "log for a list of received callbacks, if any.");
- }
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- assumeTrue(canChangeActiveNetworkMeteredness());
-
- registerBroadcastReceiver();
-
- removeRestrictBackgroundWhitelist(mUid);
- removeRestrictBackgroundBlacklist(mUid);
- assertRestrictBackgroundChangedReceived(0);
-
- // Initial state
- setBatterySaverMode(false);
- setRestrictBackground(false);
- setAppIdle(false);
-
- // Get transports of the active network, this has to be done before changing meteredness,
- // since wifi will be disconnected when changing from non-metered to metered.
- final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
-
- // Mark network as metered.
- mMeterednessConfiguration.configureNetworkMeteredness(true);
-
- // Register callback, copy the capabilities from the active network to expect the "original"
- // network before disconnecting, but null out some fields to prevent over-specified.
- registerNetworkCallback(new NetworkRequest.Builder()
- .setCapabilities(networkCapabilities.setTransportInfo(null))
- .removeCapability(NET_CAPABILITY_NOT_METERED)
- .setSignalStrength(SIGNAL_STRENGTH_UNSPECIFIED).build(), mTestNetworkCallback);
- // Wait for onAvailable() callback to ensure network is available before the test
- // and store the default network.
- mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
- // Check that the network is metered.
- mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
- false /* hasCapability */, NET_CAPABILITY_NOT_METERED);
- mTestNetworkCallback.drainAndWaitForIdle();
-
- // Before Android T, DNS queries over private DNS should be but are not restricted by Power
- // Saver or Data Saver. The issue is fixed in mainline update and apps can no longer request
- // DNS queries when its network is restricted by Power Saver. The fix takes effect backwards
- // starting from Android T. But for Data Saver, the fix is not backward compatible since
- // there are some platform changes involved. It is only available on devices that a specific
- // trunk flag is enabled.
- //
- // This test can not only verify that the network traffic from apps is blocked at the right
- // time, but also verify whether it is correctly blocked at the DNS stage, or at a later
- // socket connection stage.
- if (SdkLevel.isAtLeastT()) {
- // Enable private DNS
- mCtsNetUtils = new CtsNetUtils(mContext);
- mCtsNetUtils.storePrivateDnsSetting();
- mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
- mCtsNetUtils.awaitPrivateDnsSetting(
- "NetworkCallbackTest wait private DNS setting timeout", mNetwork,
- GOOGLE_PRIVATE_DNS_SERVER, true);
- }
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
-
- setRestrictBackground(false);
- setBatterySaverMode(false);
- unregisterNetworkCallback();
- stopApp();
-
- if (SdkLevel.isAtLeastT() && (mCtsNetUtils != null)) {
- mCtsNetUtils.restorePrivateDnsSetting();
- }
- }
-
- @RequiredProperties({DATA_SAVER_MODE})
- @Test
- public void testOnBlockedStatusChanged_dataSaver() throws Exception {
- try {
- // Enable restrict background
- setRestrictBackground(true);
- // TODO: Verify expectedUnavailableError when aconfig support mainline.
- // (see go/aconfig-in-mainline-problems)
- assertBackgroundNetworkAccess(false);
- assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
-
- // Add to whitelist
- addRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
-
- // Remove from whitelist
- removeRestrictBackgroundWhitelist(mUid);
- // TODO: Verify expectedUnavailableError when aconfig support mainline.
- assertBackgroundNetworkAccess(false);
- assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
-
- // Set to non-metered network
- mMeterednessConfiguration.configureNetworkMeteredness(false);
- mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
- true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
- try {
- assertBackgroundNetworkAccess(true);
- assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
-
- // Disable restrict background, should not trigger callback
- setRestrictBackground(false);
- assertBackgroundNetworkAccess(true);
- assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
- }
-
- @RequiredProperties({BATTERY_SAVER_MODE})
- @Test
- public void testOnBlockedStatusChanged_powerSaver() throws Exception {
- try {
- // Enable Power Saver
- setBatterySaverMode(true);
- if (SdkLevel.isAtLeastT()) {
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- assertNetworkAccess(false, "java.net.UnknownHostException");
- } else {
- assertBackgroundNetworkAccess(false);
- }
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
- assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
-
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
- assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
-
- // Set to non-metered network
- mMeterednessConfiguration.configureNetworkMeteredness(false);
- mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
- true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
- try {
- // Enable Power Saver
- setBatterySaverMode(true);
- if (SdkLevel.isAtLeastT()) {
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- assertNetworkAccess(false, "java.net.UnknownHostException");
- } else {
- assertBackgroundNetworkAccess(false);
- }
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
- assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
-
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
- assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
- }
-
- @Test
- public void testOnBlockedStatusChanged_default() throws Exception {
- assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
-
- try {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- assertNetworkAccess(false, null);
- assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
-
- launchActivity();
- assertTopState();
- assertNetworkAccess(true, null);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
- assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
-
- finishActivity();
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(mProcessStateTransitionLongDelayMs);
- assertNetworkAccess(false, null);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
- assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
-
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
-
- // Set to non-metered network
- mMeterednessConfiguration.configureNetworkMeteredness(false);
- mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
- true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
- try {
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- assertNetworkAccess(false, null);
- assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
-
- launchActivity();
- assertTopState();
- assertNetworkAccess(true, null);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
- assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
-
- finishActivity();
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(mProcessStateTransitionLongDelayMs);
- assertNetworkAccess(false, null);
- mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
- assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
- } finally {
- mMeterednessConfiguration.resetNetworkMeteredness();
- }
- }
-
- // TODO: 1. test against VPN lockdown.
- // 2. test against multiple networks.
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
deleted file mode 100644
index 6c5f2ff..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
-import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-import static android.os.Process.SYSTEM_UID;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertNetworkingBlockedStatusForUid;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import android.os.SystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class NetworkPolicyManagerTest extends AbstractRestrictBackgroundNetworkTestCase {
- private static final boolean METERED = true;
- private static final boolean NON_METERED = false;
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- registerBroadcastReceiver();
-
- removeRestrictBackgroundWhitelist(mUid);
- removeRestrictBackgroundBlacklist(mUid);
- assertRestrictBackgroundChangedReceived(0);
-
- // Initial state
- setBatterySaverMode(false);
- setRestrictBackground(false);
- setRestrictedNetworkingMode(false);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
-
- setBatterySaverMode(false);
- setRestrictBackground(false);
- setRestrictedNetworkingMode(false);
- unregisterNetworkCallback();
- stopApp();
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withUidNotBlocked() throws Exception {
- // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
- // test the cases of non-metered network and uid not matched by any rule.
- // If mUid is not blocked by data saver mode or power saver mode, no matter the network is
- // metered or non-metered, mUid shouldn't be blocked.
- assertFalse(isUidNetworkingBlocked(mUid, METERED)); // Match NTWK_ALLOWED_DEFAULT
- assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
- }
-
- @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE})
- @Test
- public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
- // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
- // test the case of uid is system uid.
- // SYSTEM_UID will never be blocked.
- assertFalse(isUidNetworkingBlocked(SYSTEM_UID, METERED)); // Match NTWK_ALLOWED_SYSTEM
- assertFalse(isUidNetworkingBlocked(SYSTEM_UID, NON_METERED)); // Match NTWK_ALLOWED_SYSTEM
- try {
- setRestrictBackground(true);
- setBatterySaverMode(true);
- setRestrictedNetworkingMode(true);
- assertNetworkingBlockedStatusForUid(SYSTEM_UID, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_SYSTEM
- assertFalse(
- isUidNetworkingBlocked(SYSTEM_UID, NON_METERED)); // Match NTWK_ALLOWED_SYSTEM
- } finally {
- setRestrictBackground(false);
- setBatterySaverMode(false);
- setRestrictedNetworkingMode(false);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
- }
- }
-
- @RequiredProperties({DATA_SAVER_MODE})
- @Test
- public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
- // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
- // test the cases of non-metered network, uid is matched by restrict background blacklist,
- // uid is matched by restrict background whitelist, app is in the foreground with restrict
- // background enabled and the app is in the background with restrict background enabled.
- try {
- // Enable restrict background and mUid will be blocked because it's not in the
- // foreground.
- setRestrictBackground(true);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- true /* expectedResult */); // Match NTWK_BLOCKED_BG_RESTRICT
-
- // Although restrict background is enabled and mUid is in the background, but mUid will
- // not be blocked if network is non-metered.
- assertFalse(
- isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
-
- // Add mUid into the restrict background blacklist.
- addRestrictBackgroundBlacklist(mUid);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- true /* expectedResult */); // Match NTWK_BLOCKED_DENYLIST
-
- // Although mUid is in the restrict background blacklist, but mUid won't be blocked if
- // the network is non-metered.
- assertFalse(
- isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
- removeRestrictBackgroundBlacklist(mUid);
-
- // Add mUid into the restrict background whitelist.
- addRestrictBackgroundWhitelist(mUid);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_ALLOWLIST
- assertFalse(
- isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
- removeRestrictBackgroundWhitelist(mUid);
-
- // Make TEST_APP2_PKG go to foreground and mUid will be allowed temporarily.
- launchActivity();
- assertTopState();
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_TMP_ALLOWLIST
-
- // Back to background.
- finishActivity();
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- true /* expectedResult */); // Match NTWK_BLOCKED_BG_RESTRICT
- } finally {
- setRestrictBackground(false);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
- }
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withRestrictedNetworkingMode() throws Exception {
- // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
- // test the cases of restricted networking mode enabled.
- try {
- // All apps should be blocked if restricted networking mode is enabled except for those
- // apps who have CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
- // This test won't test if an app who has CONNECTIVITY_USE_RESTRICTED_NETWORKS will not
- // be blocked because CONNECTIVITY_USE_RESTRICTED_NETWORKS is a signature/privileged
- // permission that CTS cannot acquire. Also it's not good for this test to use those
- // privileged apps which have CONNECTIVITY_USE_RESTRICTED_NETWORKS to test because there
- // is no guarantee that those apps won't remove this permission someday, and if it
- // happens, then this test will fail.
- setRestrictedNetworkingMode(true);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- true /* expectedResult */); // Match NTWK_BLOCKED_RESTRICTED_MODE
- assertTrue(isUidNetworkingBlocked(mUid,
- NON_METERED)); // Match NTWK_BLOCKED_RESTRICTED_MODE
- } finally {
- setRestrictedNetworkingMode(false);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
- }
- }
-
- @RequiredProperties({BATTERY_SAVER_MODE})
- @Test
- public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
- // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
- // test the cases of power saver mode enabled, uid in the power saver mode whitelist and
- // uid in the power saver mode whitelist with non-metered network.
- try {
- // mUid should be blocked if power saver mode is enabled.
- setBatterySaverMode(true);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- true /* expectedResult */); // Match NTWK_BLOCKED_POWER
- assertTrue(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_BLOCKED_POWER
-
- // Add TEST_APP2_PKG into power saver mode whitelist, its uid rule is RULE_ALLOW_ALL and
- // it shouldn't be blocked.
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
- assertFalse(
- isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- } finally {
- setBatterySaverMode(false);
- assertNetworkingBlockedStatusForUid(mUid, METERED,
- false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
- }
- }
-
- @RequiredProperties({DATA_SAVER_MODE})
- @Test
- public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
- try {
- // isUidRestrictedOnMeteredNetworks() will only return true when restrict background is
- // enabled and mUid is not in the restrict background whitelist and TEST_APP2_PKG is not
- // in the foreground. For other cases, it will return false.
- setRestrictBackground(true);
- assertIsUidRestrictedOnMeteredNetworks(mUid, true /* expectedResult */);
-
- // Make TEST_APP2_PKG go to foreground and isUidRestrictedOnMeteredNetworks() will
- // return false.
- launchActivity();
- assertTopState();
- assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
- // Back to background.
- finishActivity();
- assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-
- // Add mUid into restrict background whitelist and isUidRestrictedOnMeteredNetworks()
- // will return false.
- addRestrictBackgroundWhitelist(mUid);
- assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
- removeRestrictBackgroundWhitelist(mUid);
- } finally {
- // Restrict background is disabled and isUidRestrictedOnMeteredNetworks() will return
- // false.
- setRestrictBackground(false);
- assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
- }
- }
-
- @Test
- public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
- assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
-
- try {
- assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
- SystemClock.sleep(mProcessStateTransitionShortDelayMs);
- assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
- assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
-
- launchActivity();
- assertTopState();
- assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
- assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
-
- finishActivity();
- assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
- SystemClock.sleep(mProcessStateTransitionLongDelayMs);
- assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
- assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
- assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
- } finally {
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
deleted file mode 100644
index 0207b00..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
-
-import org.junit.rules.RunRules;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-import java.util.List;
-
-/**
- * Custom runner to allow dumping logs after a test failure before the @After methods get to run.
- */
-public class NetworkPolicyTestRunner extends AndroidJUnit4ClassRunner {
- private TestRule mDumpOnFailureRule = new DumpOnFailureRule();
-
- public NetworkPolicyTestRunner(Class<?> klass) throws InitializationError {
- super(klass);
- }
-
- @Override
- public Statement methodInvoker(FrameworkMethod method, Object test) {
- return new RunRules(super.methodInvoker(method, test), List.of(mDumpOnFailureRule),
- describeChild(method));
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
deleted file mode 100644
index 26a88f2..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside;
-
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
-import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
-import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.app.ActivityManager;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkPolicyManager;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-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;
-import android.util.Log;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.uiautomator.UiDevice;
-
-import com.android.compatibility.common.util.AppStandbyUtils;
-import com.android.compatibility.common.util.BatteryUtils;
-import com.android.compatibility.common.util.PollingCheck;
-import com.android.compatibility.common.util.ShellIdentityUtils;
-import com.android.compatibility.common.util.ThrowingRunnable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class NetworkPolicyTestUtils {
-
- // android.telephony.CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS
- // TODO: Expose it as a @TestApi instead of copying the constant
- private static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
- "carrier_metered_apn_types_strings";
-
- private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 10_000;
-
- private static ConnectivityManager mCm;
- private static WifiManager mWm;
- private static CarrierConfigManager mCarrierConfigManager;
- private static NetworkPolicyManager sNpm;
-
- private static Boolean mBatterySaverSupported;
- private static Boolean mDataSaverSupported;
- private static Boolean mDozeModeSupported;
- private static Boolean mAppStandbySupported;
-
- private NetworkPolicyTestUtils() {}
-
- public static boolean isBatterySaverSupported() {
- if (mBatterySaverSupported == null) {
- mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
- }
- return mBatterySaverSupported;
- }
-
- private static boolean isWear() {
- return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- }
-
- /**
- * As per CDD requirements, if the device doesn't support data saver mode then
- * ConnectivityManager.getRestrictBackgroundStatus() will always return
- * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
- * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
- * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
- */
- public static boolean isDataSaverSupported() {
- if (isWear()) {
- return false;
- }
- if (mDataSaverSupported == null) {
- setRestrictBackgroundInternal(false);
- assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
- try {
- setRestrictBackgroundInternal(true);
- mDataSaverSupported = !isMyRestrictBackgroundStatus(
- RESTRICT_BACKGROUND_STATUS_DISABLED);
- } finally {
- setRestrictBackgroundInternal(false);
- }
- }
- return mDataSaverSupported;
- }
-
- public static boolean isDozeModeSupported() {
- if (mDozeModeSupported == null) {
- final String result = executeShellCommand("cmd deviceidle enabled deep");
- mDozeModeSupported = result.equals("1");
- }
- return mDozeModeSupported;
- }
-
- public static boolean isAppStandbySupported() {
- if (mAppStandbySupported == null) {
- mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
- }
- return mAppStandbySupported;
- }
-
- public static boolean isLowRamDevice() {
- final ActivityManager am = (ActivityManager) getContext().getSystemService(
- Context.ACTIVITY_SERVICE);
- 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);
- return lm.isLocationEnabled();
- }
-
- public static void setLocationEnabled(boolean enabled) {
- final LocationManager lm = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
- lm.setLocationEnabledForUser(enabled, Process.myUserHandle());
- assertEquals("Couldn't change location enabled state", lm.isLocationEnabled(), enabled);
- Log.d(TAG, "Changed location enabled state to " + enabled);
- }
-
- public static boolean isActiveNetworkMetered(boolean metered) {
- return getConnectivityManager().isActiveNetworkMetered() == metered;
- }
-
- public static boolean canChangeActiveNetworkMeteredness() {
- final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
- return networkCapabilities.hasTransport(TRANSPORT_WIFI)
- || networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
- }
-
- /**
- * Updates the meteredness of the active network. Right now we can only change meteredness
- * of either Wifi or cellular network, so if the active network is not either of these, this
- * will throw an exception.
- *
- * @return a {@link ThrowingRunnable} object that can used to reset the meteredness change
- * made by this method.
- */
- public static ThrowingRunnable setupActiveNetworkMeteredness(boolean metered) throws Exception {
- if (isActiveNetworkMetered(metered)) {
- return null;
- }
- final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
- if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
- final String ssid = getWifiSsid();
- setWifiMeteredStatus(ssid, metered);
- return () -> setWifiMeteredStatus(ssid, !metered);
- } else if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- final int subId = SubscriptionManager.getActiveDataSubscriptionId();
- setCellularMeteredStatus(subId, metered);
- return () -> setCellularMeteredStatus(subId, !metered);
- } else {
- // Right now, we don't have a way to change meteredness of networks other
- // than Wi-Fi or Cellular, so just throw an exception.
- throw new IllegalStateException("Can't change meteredness of current active network");
- }
- }
-
- private static String getWifiSsid() {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- final String ssid = getWifiManager().getConnectionInfo().getSSID();
- assertNotEquals(WifiManager.UNKNOWN_SSID, ssid);
- return ssid;
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-
- static NetworkCapabilities getActiveNetworkCapabilities() {
- final Network activeNetwork = getConnectivityManager().getActiveNetwork();
- assertNotNull("No active network available", activeNetwork);
- return getConnectivityManager().getNetworkCapabilities(activeNetwork);
- }
-
- private static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- final WifiConfiguration currentConfig = getWifiConfiguration(ssid);
- currentConfig.meteredOverride = metered
- ? METERED_OVERRIDE_METERED : METERED_OVERRIDE_NONE;
- BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
- getWifiManager().save(currentConfig, createActionListener(
- blockingQueue, Integer.MAX_VALUE));
- Integer resultCode = blockingQueue.poll(TIMEOUT_CHANGE_METEREDNESS_MS,
- TimeUnit.MILLISECONDS);
- if (resultCode == null) {
- fail("Timed out waiting for meteredness to change; ssid=" + ssid
- + ", metered=" + metered);
- } else if (resultCode != Integer.MAX_VALUE) {
- fail("Error overriding the meteredness; ssid=" + ssid
- + ", metered=" + metered + ", error=" + resultCode);
- }
- final boolean success = assertActiveNetworkMetered(metered, false /* throwOnFailure */);
- if (!success) {
- Log.i(TAG, "Retry connecting to wifi; ssid=" + ssid);
- blockingQueue = new LinkedBlockingQueue<>();
- getWifiManager().connect(currentConfig, createActionListener(
- blockingQueue, Integer.MAX_VALUE));
- resultCode = blockingQueue.poll(TIMEOUT_CHANGE_METEREDNESS_MS,
- TimeUnit.MILLISECONDS);
- if (resultCode == null) {
- fail("Timed out waiting for wifi to connect; ssid=" + ssid);
- } else if (resultCode != Integer.MAX_VALUE) {
- fail("Error connecting to wifi; ssid=" + ssid
- + ", error=" + resultCode);
- }
- assertActiveNetworkMetered(metered, true /* throwOnFailure */);
- }
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-
- private static WifiConfiguration getWifiConfiguration(String ssid) {
- final List<String> ssids = new ArrayList<>();
- for (WifiConfiguration config : getWifiManager().getConfiguredNetworks()) {
- if (config.SSID.equals(ssid)) {
- return config;
- }
- ssids.add(config.SSID);
- }
- fail("Couldn't find the wifi config; ssid=" + ssid
- + ", all=" + Arrays.toString(ssids.toArray()));
- return null;
- }
-
- private static ActionListener createActionListener(BlockingQueue<Integer> blockingQueue,
- int successCode) {
- return new ActionListener() {
- @Override
- public void onSuccess() {
- blockingQueue.offer(successCode);
- }
-
- @Override
- public void onFailure(int reason) {
- blockingQueue.offer(reason);
- }
- };
- }
-
- private static void setCellularMeteredStatus(int subId, boolean metered) throws Exception {
- final PersistableBundle bundle = new PersistableBundle();
- bundle.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[] {ApnSetting.TYPE_MMS_STRING});
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(getCarrierConfigManager(),
- (cm) -> cm.overrideConfig(subId, metered ? null : bundle));
- assertActiveNetworkMetered(metered, true /* throwOnFailure */);
- }
-
- private static boolean assertActiveNetworkMetered(boolean expectedMeteredStatus,
- boolean throwOnFailure) throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final NetworkCallback networkCallback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
- if (metered == expectedMeteredStatus) {
- latch.countDown();
- }
- }
- };
- // 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 will wait for the setting to change.
- getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
- try {
- if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
- final String errorMsg = "Timed out waiting for active network metered status "
- + "to change to " + expectedMeteredStatus + "; network = "
- + getConnectivityManager().getActiveNetwork();
- if (throwOnFailure) {
- fail(errorMsg);
- }
- Log.w(TAG, errorMsg);
- return false;
- }
- return true;
- } finally {
- getConnectivityManager().unregisterNetworkCallback(networkCallback);
- }
- }
-
- public static void setRestrictBackground(boolean enabled) {
- if (!isDataSaverSupported()) {
- return;
- }
- setRestrictBackgroundInternal(enabled);
- }
-
- static void setRestrictBackgroundInternal(boolean enabled) {
- executeShellCommand("cmd netpolicy set restrict-background " + enabled);
- final String output = executeShellCommand("cmd netpolicy get restrict-background");
- final String expectedSuffix = enabled ? "enabled" : "disabled";
- assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
- output.endsWith(expectedSuffix));
- }
-
- public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
- final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
- if (expectedStatus != actualStatus) {
- Log.d(TAG, "MyRestrictBackgroundStatus: "
- + "Expected: " + restrictBackgroundValueToString(expectedStatus)
- + "; Actual: " + restrictBackgroundValueToString(actualStatus));
- return false;
- }
- return true;
- }
-
- // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
- private static String unquoteSSID(String ssid) {
- // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
- // Otherwise it's guaranteed not to start with a quote.
- if (ssid.charAt(0) == '"') {
- return ssid.substring(1, ssid.length() - 1);
- } else {
- return ssid;
- }
- }
-
- public static String restrictBackgroundValueToString(int status) {
- switch (status) {
- case RESTRICT_BACKGROUND_STATUS_DISABLED:
- return "DISABLED";
- case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
- return "WHITELISTED";
- case RESTRICT_BACKGROUND_STATUS_ENABLED:
- return "ENABLED";
- default:
- return "UNKNOWN_STATUS_" + status;
- }
- }
-
- public static void clearSnoozeTimestamps() {
- executeShellCommand("dumpsys netpolicy --unsnooze");
- }
-
- public static String executeShellCommand(String command) {
- final String result = runShellCommandOrThrow(command).trim();
- Log.d(TAG, "Output of '" + command + "': '" + result + "'");
- return result;
- }
-
- public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
- final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
- assertEquals(restrictBackgroundValueToString(expectedStatus),
- restrictBackgroundValueToString(actualStatus));
- }
-
- public static ConnectivityManager getConnectivityManager() {
- if (mCm == null) {
- mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- }
- return mCm;
- }
-
- public static WifiManager getWifiManager() {
- if (mWm == null) {
- mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
- }
- return mWm;
- }
-
- public static CarrierConfigManager getCarrierConfigManager() {
- if (mCarrierConfigManager == null) {
- mCarrierConfigManager = (CarrierConfigManager) getContext().getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- }
- return mCarrierConfigManager;
- }
-
- public static NetworkPolicyManager getNetworkPolicyManager() {
- if (sNpm == null) {
- sNpm = getContext().getSystemService(NetworkPolicyManager.class);
- }
- return sNpm;
- }
-
- public static Context getContext() {
- return getInstrumentation().getContext();
- }
-
- public static Instrumentation getInstrumentation() {
- return InstrumentationRegistry.getInstrumentation();
- }
-
- public static UiDevice getUiDevice() {
- return UiDevice.getInstance(getInstrumentation());
- }
-
- // When power saver mode or restrict background enabled or adding any white/black list into
- // those modes, NetworkPolicy may need to take some time to update the rules of uids. So having
- // this function and using PollingCheck to try to make sure the uid has updated and reduce the
- // flaky rate.
- public static void assertNetworkingBlockedStatusForUid(int uid, boolean metered,
- boolean expectedResult) {
- final String errMsg = String.format("Unexpected result from isUidNetworkingBlocked; "
- + "uid= " + uid + ", metered=" + metered + ", expected=" + expectedResult);
- PollingCheck.waitFor(() -> (expectedResult == isUidNetworkingBlocked(uid, metered)),
- errMsg);
- }
-
- public static void assertIsUidRestrictedOnMeteredNetworks(int uid, boolean expectedResult) {
- final String errMsg = String.format(
- "Unexpected result from isUidRestrictedOnMeteredNetworks; "
- + "uid= " + uid + ", expected=" + expectedResult);
- PollingCheck.waitFor(() -> (expectedResult == isUidRestrictedOnMeteredNetworks(uid)),
- errMsg);
- }
-
- public static boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- return getNetworkPolicyManager().isUidNetworkingBlocked(uid, meteredNetwork);
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-
- public static boolean isUidRestrictedOnMeteredNetworks(int uid) {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- return getNetworkPolicyManager().isUidRestrictedOnMeteredNetworks(uid);
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
deleted file mode 100644
index a03833f..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
-import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isLowRamDevice;
-
-public enum Property {
- BATTERY_SAVER_MODE(1 << 0) {
- public boolean isSupported() { return isBatterySaverSupported(); }
- },
-
- DATA_SAVER_MODE(1 << 1) {
- public boolean isSupported() { return isDataSaverSupported(); }
- },
-
- NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
- public boolean isSupported() { return !isDataSaverSupported(); }
- },
-
- DOZE_MODE(1 << 2) {
- public boolean isSupported() { return isDozeModeSupported(); }
- },
-
- APP_STANDBY_MODE(1 << 3) {
- public boolean isSupported() { return isAppStandbySupported(); }
- },
-
- NOT_LOW_RAM_DEVICE(1 << 4) {
- public boolean isSupported() { return !isLowRamDevice(); }
- },
-
- METERED_NETWORK(1 << 5) {
- public boolean isSupported() {
- return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
- }
- },
-
- NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
- public boolean isSupported() {
- return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
- }
- };
-
- private int mValue;
-
- Property(int value) { mValue = value; }
-
- public int getValue() { return mValue; }
-
- abstract boolean isSupported();
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
deleted file mode 100644
index 799a513..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-@Retention(RUNTIME)
-@Target({METHOD, TYPE})
-@Inherited
-public @interface RequiredProperties {
- Property[] value();
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
deleted file mode 100644
index 5dea67c..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside;
-
-import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
-
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.compatibility.common.util.BeforeAfterRule;
-
-import org.junit.Assume;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-public class RequiredPropertiesRule extends BeforeAfterRule {
-
- private static ArraySet<Property> mRequiredProperties;
-
- @Override
- public void onBefore(Statement base, Description description) {
- mRequiredProperties = getAllRequiredProperties(description);
-
- final String testName = description.getClassName() + "#" + description.getMethodName();
- assertTestIsValid(testName, mRequiredProperties);
- Log.i(TAG, "Running test " + testName + " with required properties: "
- + propertiesToString(mRequiredProperties));
- }
-
- private ArraySet<Property> getAllRequiredProperties(Description description) {
- final ArraySet<Property> allRequiredProperties = new ArraySet<>();
- RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
- if (requiredProperties != null) {
- Collections.addAll(allRequiredProperties, requiredProperties.value());
- }
-
- for (Class<?> clazz = description.getTestClass();
- clazz != null; clazz = clazz.getSuperclass()) {
- requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
- if (requiredProperties == null) {
- continue;
- }
- for (Property requiredProperty : requiredProperties.value()) {
- for (Property p : Property.values()) {
- if (p.getValue() == ~requiredProperty.getValue()
- && allRequiredProperties.contains(p)) {
- continue;
- }
- }
- allRequiredProperties.add(requiredProperty);
- }
- }
- return allRequiredProperties;
- }
-
- private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
- if (requiredProperies == null) {
- return;
- }
- final ArrayList<Property> unsupportedProperties = new ArrayList<>();
- for (Property property : requiredProperies) {
- if (!property.isSupported()) {
- unsupportedProperties.add(property);
- }
- }
- Assume.assumeTrue("Unsupported properties: "
- + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
- }
-
- public static ArraySet<Property> getRequiredProperties() {
- return mRequiredProperties;
- }
-
- private static String propertiesToString(Iterable<Property> properties) {
- return "[" + TextUtils.join(",", properties) + "]";
- }
-}
diff --git a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
deleted file mode 100644
index f183f4e..0000000
--- a/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.netpolicy.hostside;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public final class RestrictedModeTest extends AbstractRestrictBackgroundNetworkTestCase {
- @Before
- public void setUp() throws Exception {
- super.setUp();
- setRestrictedNetworkingMode(false);
- }
-
- @After
- public void tearDown() throws Exception {
- setRestrictedNetworkingMode(false);
- super.tearDown();
- }
-
- @Test
- public void testNetworkAccess() throws Exception {
- // go to foreground state and enable restricted mode
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- setRestrictedNetworkingMode(true);
- assertTopNetworkAccess(false);
-
- // go to background state
- finishActivity();
- assertBackgroundNetworkAccess(false);
-
- // disable restricted mode and assert network access in foreground and background states
- setRestrictedNetworkingMode(false);
- launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
- assertTopNetworkAccess(true);
-
- // go to background state
- finishActivity();
- assertBackgroundNetworkAccess(true);
- }
-
- @Test
- public void testNetworkAccess_withBatterySaver() throws Exception {
- setBatterySaverMode(true);
- try {
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
-
- setRestrictedNetworkingMode(true);
- // App would be denied network access since Restricted mode is on.
- assertBackgroundNetworkAccess(false);
- setRestrictedNetworkingMode(false);
- // Given that Restricted mode is turned off, app should be able to access network again.
- assertBackgroundNetworkAccess(true);
- } finally {
- setBatterySaverMode(false);
- }
- }
-}
diff --git a/tests/cts/hostside-network-policy/app2/Android.bp b/tests/cts/hostside-network-policy/app2/Android.bp
deleted file mode 100644
index 6ef0b06..0000000
--- a/tests/cts/hostside-network-policy/app2/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// 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 {
- default_team: "trendy_team_framework_backstage_power",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_test_helper_app {
- name: "CtsHostsideNetworkPolicyTestsApp2",
- defaults: ["cts_support_defaults"],
- platform_apis: true,
- static_libs: [
- "androidx.annotation_annotation",
- "CtsHostsideNetworkPolicyTestsAidl",
- "modules-utils-build",
- ],
- srcs: ["src/**/*.java"],
- // Tag this module as a cts test artifact
- test_suites: [
- "cts",
- "general-tests",
- "sts",
- ],
- certificate: ":cts-netpolicy-app",
-}
diff --git a/tests/cts/hostside-network-policy/app2/AndroidManifest.xml b/tests/cts/hostside-network-policy/app2/AndroidManifest.xml
deleted file mode 100644
index 668f2da..0000000
--- a/tests/cts/hostside-network-policy/app2/AndroidManifest.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.netpolicy.hostside.app2">
-
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-
- <!--
- 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.
-
- 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"
- android:exported="true"/>
- <service android:name=".MyForegroundService"
- android:foregroundServiceType="specialUse"
- android:exported="true">
- <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
- android:value="Connectivity" />
- </service>
- <receiver android:name=".MyBroadcastReceiver"
- android:exported="true">
- <intent-filter>
- <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED"/>
- <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_COUNTERS"/>
- <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS"/>
- <action android:name="com.android.cts.netpolicy.hostside.app2.action.CHECK_NETWORK"/>
- <action android:name="com.android.cts.netpolicy.hostside.app2.action.SEND_NOTIFICATION"/>
- <action android:name="com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST"/>
- </intent-filter>
- </receiver>
- <service android:name=".MyJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
- </application>
-
- <!--
- Adding this to make sure that receiving the broadcast is not restricted by
- package visibility restrictions.
- -->
- <queries>
- <package android:name="android" />
- </queries>
-
-</manifest>
diff --git a/tests/cts/hostside-network-policy/app2/res/drawable/ic_notification.png b/tests/cts/hostside-network-policy/app2/res/drawable/ic_notification.png
deleted file mode 100644
index 6ae570b..0000000
--- a/tests/cts/hostside-network-policy/app2/res/drawable/ic_notification.png
+++ /dev/null
Binary files differ
diff --git a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
deleted file mode 100644
index 1719f9b..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside.app2;
-
-import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_OTHER;
-import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES;
-import static com.android.cts.netpolicy.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;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.cts.netpolicy.hostside.INetworkStateObserver;
-import com.android.cts.netpolicy.hostside.NetworkCheckResult;
-
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.URL;
-import java.util.concurrent.TimeUnit;
-
-public final class Common {
-
- static final String TAG = "CtsNetApp2";
-
- // Constants below must match values defined on app's
- // AbstractRestrictBackgroundNetworkTestCase.java
- static final String MANIFEST_RECEIVER = "ManifestReceiver";
- static final String DYNAMIC_RECEIVER = "DynamicReceiver";
-
- static final String ACTION_RECEIVER_READY =
- "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
- static final String ACTION_FINISH_ACTIVITY =
- "com.android.cts.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
- static final String ACTION_FINISH_JOB =
- "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
- static final String ACTION_SHOW_TOAST =
- "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
- // Copied from com.android.server.net.NetworkPolicyManagerService class
- 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";
- static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
- static final String NOTIFICATION_TYPE_ACTION = "ACTION";
- static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
- static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
-
- static final String TEST_PKG = "com.android.cts.netpolicy.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();
- try {
- return context.getPackageManager().getPackageUid(packageName, 0);
- } catch (NameNotFoundException e) {
- throw new IllegalStateException("Could not get UID for " + packageName, e);
- }
- }
-
- 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);
- switch (componentType) {
- case TYPE_COMPONENT_ACTIVTY: {
- final int procState = activityManager.getUidProcessState(Process.myUid());
- if (procState != ActivityManager.PROCESS_STATE_TOP) {
- observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_PROC_STATE,
- createNetworkCheckResult(false, "Unexpected procstate: " + procState,
- null));
- 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(RESULT_ERROR_UNEXPECTED_PROC_STATE,
- createNetworkCheckResult(false, "Unexpected procstate: " + procState,
- null));
- return false;
- }
- return true;
- }
- case TYPE_COMPONENT_EXPEDITED_JOB: {
- final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
- if ((capabilities
- & ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) == 0) {
- observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_CAPABILITIES,
- createNetworkCheckResult(false,
- "Unexpected capabilities: " + capabilities, null));
- return false;
- }
- return true;
- }
- default: {
- observer.onNetworkStateChecked(RESULT_ERROR_OTHER,
- createNetworkCheckResult(false, "Unknown component type: " + componentType,
- null));
- 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;
- }
- 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)) {
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error occurred while informing the validation result: " + e);
- }
- AsyncTask.execute(() -> {
- try {
- observer.onNetworkStateChecked(
- INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
- 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-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
deleted file mode 100644
index d274c50..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside.app2;
-
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
-import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
-import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.RemoteCallback;
-import android.util.Log;
-import android.view.WindowManager;
-
-import androidx.annotation.GuardedBy;
-
-/**
- * Activity used to bring process to foreground.
- */
-public class MyActivity extends Activity {
-
- @GuardedBy("this")
- private BroadcastReceiver finishCommandReceiver = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.d(TAG, "MyActivity.onCreate()");
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- @Override
- public void finish() {
- synchronized (this) {
- if (finishCommandReceiver != null) {
- unregisterReceiver(finishCommandReceiver);
- finishCommandReceiver = null;
- }
- }
- super.finish();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(TAG, "MyActivity.onStart()");
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- Log.d(TAG, "MyActivity.onNewIntent()");
- setIntent(intent);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.d(TAG, "MyActivity.onResume(): " + getIntent());
- Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
- synchronized (this) {
- finishCommandReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "Finishing MyActivity");
- MyActivity.this.finish();
- }
- };
- registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY),
- Context.RECEIVER_EXPORTED);
- }
- final RemoteCallback callback = getIntent().getParcelableExtra(
- Intent.EXTRA_REMOTE_CALLBACK);
- if (callback != null) {
- callback.sendResult(null);
- }
- }
-
- @Override
- protected void onDestroy() {
- Log.d(TAG, "MyActivity.onDestroy()");
- super.onDestroy();
- }
-}
diff --git a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
deleted file mode 100644
index 27aec8c..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy.hostside.app2;
-
-import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SHOW_TOAST;
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
-import static com.android.cts.netpolicy.hostside.app2.Common.MANIFEST_RECEIVER;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_REMOTE_INPUT;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_BUNDLE;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_CONTENT;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
-import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
-import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
-
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.ConnectivityManager;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
-
-/**
- * Receiver used to:
- * <ol>
- * <li>Count number of {@code RESTRICT_BACKGROUND_CHANGED} broadcasts received.
- * <li>Show a toast.
- * </ol>
- */
-public class MyBroadcastReceiver extends BroadcastReceiver {
-
- private final String mName;
-
- public MyBroadcastReceiver() {
- this(MANIFEST_RECEIVER);
- }
-
- MyBroadcastReceiver(String name) {
- Log.d(TAG, "Constructing MyBroadcastReceiver named " + name);
- mName = name;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "onReceive() for " + mName + ": " + intent);
- final String action = intent.getAction();
- switch (action) {
- case ACTION_SNOOZE_WARNING:
- increaseCounter(context, action);
- break;
- case ACTION_RESTRICT_BACKGROUND_CHANGED:
- increaseCounter(context, action);
- break;
- case ACTION_RECEIVER_READY:
- final String message = mName + " is ready to rumble";
- Log.d(TAG, message);
- setResultData(message);
- break;
- case ACTION_SHOW_TOAST:
- showToast(context);
- break;
- default:
- Log.e(TAG, "received unexpected action: " + action);
- }
- }
-
- @Override
- public String toString() {
- return "[MyBroadcastReceiver: mName=" + mName + "]";
- }
-
- private void increaseCounter(Context context, String action) {
- final SharedPreferences prefs = context.getApplicationContext()
- .getSharedPreferences(mName, Context.MODE_PRIVATE);
- final int value = prefs.getInt(action, 0) + 1;
- Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
- prefs.edit().putInt(action, value).apply();
- }
-
- static int getCounter(Context context, String action, String receiverName) {
- final SharedPreferences prefs = context.getSharedPreferences(receiverName,
- Context.MODE_PRIVATE);
- final int value = prefs.getInt(action, 0);
- Log.d(TAG, "getCounter('" + action + "', '" + receiverName + "'): " + value);
- return value;
- }
-
- static String getRestrictBackgroundStatus(Context context) {
- final ConnectivityManager cm = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- final int apiStatus = cm.getRestrictBackgroundStatus();
- Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
- return String.valueOf(apiStatus);
- }
-
- /**
- * Sends a system notification containing actions with pending intents to launch the app's
- * main activitiy or service.
- */
- static void sendNotification(Context context, String channelId, int notificationId,
- String notificationType ) {
- Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
- final Intent serviceIntent = new Intent(context, MyService.class);
- final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
- PendingIntent.FLAG_MUTABLE);
- final Bundle bundle = new Bundle();
- bundle.putCharSequence("parcelable", "I am not");
-
- final Notification.Builder builder = new Notification.Builder(context, channelId)
- .setSmallIcon(R.drawable.ic_notification);
-
- Action action = null;
- switch (notificationType) {
- case NOTIFICATION_TYPE_CONTENT:
- builder
- .setContentTitle("Light, Cameras...")
- .setContentIntent(pendingIntent);
- break;
- case NOTIFICATION_TYPE_DELETE:
- builder.setDeleteIntent(pendingIntent);
- break;
- case NOTIFICATION_TYPE_FULL_SCREEN:
- builder.setFullScreenIntent(pendingIntent, true);
- break;
- case NOTIFICATION_TYPE_BUNDLE:
- bundle.putParcelable("Magnum P.I. (Pending Intent)", pendingIntent);
- builder.setExtras(bundle);
- break;
- case NOTIFICATION_TYPE_ACTION:
- action = new Action.Builder(
- R.drawable.ic_notification, "ACTION", pendingIntent)
- .build();
- builder.addAction(action);
- break;
- case NOTIFICATION_TYPE_ACTION_BUNDLE:
- bundle.putParcelable("Magnum A.P.I. (Action Pending Intent)", pendingIntent);
- action = new Action.Builder(
- R.drawable.ic_notification, "ACTION WITH BUNDLE", null)
- .addExtras(bundle)
- .build();
- builder.addAction(action);
- break;
- case NOTIFICATION_TYPE_ACTION_REMOTE_INPUT:
- bundle.putParcelable("Magnum R.I. (Remote Input)", null);
- final RemoteInput remoteInput = new RemoteInput.Builder("RI")
- .addExtras(bundle)
- .build();
- action = new Action.Builder(
- R.drawable.ic_notification, "ACTION WITH REMOTE INPUT", pendingIntent)
- .addRemoteInput(remoteInput)
- .build();
- builder.addAction(action);
- break;
- default:
- Log.e(TAG, "Unknown notification type: " + notificationType);
- return;
- }
-
- final Notification notification = builder.build();
- ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
- .notify(notificationId, notification);
- }
-
- private void showToast(Context context) {
- Toast.makeText(context, "Toast from CTS test", Toast.LENGTH_SHORT).show();
- setResultData("Shown");
- }
-}
diff --git a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
deleted file mode 100644
index 54cee3c..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside.app2;
-
-import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
-import static com.android.cts.netpolicy.hostside.app2.Common.TEST_PKG;
-import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
-
-import android.R;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.cts.netpolicy.hostside.INetworkStateObserver;
-
-/**
- * Service used to change app state to FOREGROUND_SERVICE.
- */
-public class MyForegroundService extends Service {
- private static final String NOTIFICATION_CHANNEL_ID = "cts/MyForegroundService";
- private static final int FLAG_START_FOREGROUND = 1;
- private static final int FLAG_STOP_FOREGROUND = 2;
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
- NotificationManager notificationManager = getSystemService(NotificationManager.class);
- notificationManager.createNotificationChannel(new NotificationChannel(
- NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
- NotificationManager.IMPORTANCE_DEFAULT));
- switch (intent.getFlags()) {
- case FLAG_START_FOREGROUND:
- Log.d(TAG, "Starting foreground");
- startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
- .build());
- Common.notifyNetworkStateObserver(this, intent, TYPE_COMPONENT_FOREGROUND_SERVICE);
- break;
- case FLAG_STOP_FOREGROUND:
- Log.d(TAG, "Stopping foreground");
- stopForeground(true);
- break;
- default:
- Log.wtf(TAG, "Invalid flag on intent " + intent);
- }
- return START_STICKY;
- }
-}
diff --git a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
deleted file mode 100644
index eba55ed..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.netpolicy.hostside.app2;
-
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_JOB;
-import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
-import static com.android.cts.netpolicy.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),
- Context.RECEIVER_EXPORTED);
- 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-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
deleted file mode 100644
index 71bcead..0000000
--- a/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy.hostside.app2;
-
-import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
-import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
-import static com.android.cts.netpolicy.hostside.app2.Common.DYNAMIC_RECEIVER;
-import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
-
-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;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.cts.netpolicy.hostside.IMyService;
-import com.android.cts.netpolicy.hostside.INetworkCallback;
-import com.android.cts.netpolicy.hostside.NetworkCheckResult;
-import com.android.modules.utils.build.SdkLevel;
-
-/**
- * Service used to dynamically register a broadcast receiver.
- */
-public class MyService extends Service {
- private static final String NOTIFICATION_CHANNEL_ID = "MyService";
-
- ConnectivityManager mCm;
-
- private MyBroadcastReceiver mReceiver;
- private ConnectivityManager.NetworkCallback mNetworkCallback;
-
- // TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
-
- private IMyService.Stub mBinder = new IMyService.Stub() {
- @Override
- public void registerBroadcastReceiver() {
- if (mReceiver != null) {
- Log.d(TAG, "receiver already registered: " + mReceiver);
- return;
- }
- final Context context = getApplicationContext();
- final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
- mReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
- context.registerReceiver(mReceiver,
- new IntentFilter(ACTION_RECEIVER_READY), flags);
- context.registerReceiver(mReceiver,
- new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED), flags);
- context.registerReceiver(mReceiver,
- new IntentFilter(ACTION_SNOOZE_WARNING), flags);
- Log.d(TAG, "receiver registered");
- }
-
- @Override
- public int getCounters(String receiverName, String action) {
- return MyBroadcastReceiver.getCounter(getApplicationContext(), action, receiverName);
- }
-
- @Override
- public NetworkCheckResult checkNetworkStatus(String customUrl) {
- return Common.checkNetworkStatus(getApplicationContext(), customUrl);
- }
-
- @Override
- public String getRestrictBackgroundStatus() {
- return MyBroadcastReceiver.getRestrictBackgroundStatus(getApplicationContext());
- }
-
- @Override
- public void sendNotification(int notificationId, String notificationType) {
- MyBroadcastReceiver.sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
- notificationId, notificationType);
- }
-
- @Override
- public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb) {
- if (mNetworkCallback != null) {
- Log.d(TAG, "unregister previous network callback: " + mNetworkCallback);
- unregisterNetworkCallback();
- }
- Log.d(TAG, "registering network callback for " + request);
-
- mNetworkCallback = new ConnectivityManager.NetworkCallback() {
- @Override
- public void onBlockedStatusChanged(Network network, boolean blocked) {
- try {
- cb.onBlockedStatusChanged(network, blocked);
- } catch (RemoteException e) {
- Log.d(TAG, "Cannot send onBlockedStatusChanged: " + e);
- unregisterNetworkCallback();
- }
- }
-
- @Override
- public void onAvailable(Network network) {
- try {
- cb.onAvailable(network);
- } catch (RemoteException e) {
- Log.d(TAG, "Cannot send onAvailable: " + e);
- unregisterNetworkCallback();
- }
- }
-
- @Override
- public void onLost(Network network) {
- try {
- cb.onLost(network);
- } catch (RemoteException e) {
- Log.d(TAG, "Cannot send onLost: " + e);
- unregisterNetworkCallback();
- }
- }
-
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
- try {
- cb.onCapabilitiesChanged(network, cap);
- } catch (RemoteException e) {
- Log.d(TAG, "Cannot send onCapabilitiesChanged: " + e);
- unregisterNetworkCallback();
- }
- }
- };
- mCm.registerNetworkCallback(request, mNetworkCallback);
- try {
- cb.asBinder().linkToDeath(() -> unregisterNetworkCallback(), 0);
- } catch (RemoteException e) {
- unregisterNetworkCallback();
- }
- }
-
- @Override
- public void unregisterNetworkCallback() {
- Log.d(TAG, "unregistering network callback");
- if (mNetworkCallback != null) {
- mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
- }
- }
-
- @Override
- public int scheduleJob(JobInfo jobInfo) {
- final JobScheduler jobScheduler = getApplicationContext()
- .getSystemService(JobScheduler.class);
- return jobScheduler.schedule(jobInfo);
- }
- };
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- @Override
- public void onCreate() {
- final Context context = getApplicationContext();
- ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
- .createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT));
- mCm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- }
-
- @Override
- public void onDestroy() {
- final Context context = getApplicationContext();
- ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
- .deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
- if (mReceiver != null) {
- Log.d(TAG, "onDestroy(): unregistering " + mReceiver);
- getApplicationContext().unregisterReceiver(mReceiver);
- }
-
- super.onDestroy();
- }
-}
diff --git a/tests/cts/hostside-network-policy/certs/Android.bp b/tests/cts/hostside-network-policy/certs/Android.bp
deleted file mode 100644
index bfbc341..0000000
--- a/tests/cts/hostside-network-policy/certs/Android.bp
+++ /dev/null
@@ -1,9 +0,0 @@
-package {
- default_team: "trendy_team_framework_backstage_power",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_app_certificate {
- name: "cts-netpolicy-app",
- certificate: "cts-net-app",
-}
diff --git a/tests/cts/hostside-network-policy/certs/README b/tests/cts/hostside-network-policy/certs/README
deleted file mode 100644
index b660a82..0000000
--- a/tests/cts/hostside-network-policy/certs/README
+++ /dev/null
@@ -1,2 +0,0 @@
-# Generated with:
-development/tools/make_key cts-net-app '/CN=cts-net-app'
diff --git a/tests/cts/hostside-network-policy/certs/cts-net-app.pk8 b/tests/cts/hostside-network-policy/certs/cts-net-app.pk8
deleted file mode 100644
index 1703e4e..0000000
--- a/tests/cts/hostside-network-policy/certs/cts-net-app.pk8
+++ /dev/null
Binary files differ
diff --git a/tests/cts/hostside-network-policy/certs/cts-net-app.x509.pem b/tests/cts/hostside-network-policy/certs/cts-net-app.x509.pem
deleted file mode 100644
index a15ff48..0000000
--- a/tests/cts/hostside-network-policy/certs/cts-net-app.x509.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDAjCCAeqgAwIBAgIJAMhWwIIqr1r6MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
-BAMMC2N0cy1uZXQtYXBwMB4XDTE4MDYyMDAyMjAwN1oXDTQ1MTEwNTAyMjAwN1ow
-FjEUMBIGA1UEAwwLY3RzLW5ldC1hcHAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDefOayWQss1E+FQIONK6IhlXhe0BEyHshIrnPOOmuCPa/Svfbnmziy
-hr1KTjaQ3ET/mGShwlt6AUti7nKx9aB71IJp5mSBuwW62A8jvN3yNOo45YV8+n1o
-TrEoMWMf7hQmoOSqaSJ+VFuVms/kPSEh99okDgHCej6rsEkEcDoh6pJajQyUYDwR
-SNAF8SrqCDhqFbZW/LWedvuikCUlNtzuv7/GrcLcsiWEfHv7UOBKpMjLo9BhD1XF
-IefnxImcBQrQGMnE9TLixBiEeX5yauLgbZuxBqD/zsI2TH1FjxTeuJan83kLbqqH
-FgyvPaUjwckAdQPyom7ZUYFnBc0LQ9xzAgMBAAGjUzBRMB0GA1UdDgQWBBRZrBEw
-tAB2WNXj8dQ7ZOuJ34kY5DAfBgNVHSMEGDAWgBRZrBEwtAB2WNXj8dQ7ZOuJ34kY
-5DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDeI9AnLW6l/39y
-z96w/ldxZVFPzBRiFIsJsPHVyXlD5vUHZv/ju2jFn8TZSZR5TK0bzCEoVLp34Sho
-bbS0magP82yIvCRibyoyD+TDNnZkNJwjYnikE+/oyshTSQtpkn/rDA+0Y09BUC1E
-N2I6bV9pTXLFg7oah2FmqPRPzhgeYUKENgOQkrrjUCn6y0i/k374n7aftzdniSIz
-2kCRVEeN9gws6CnoMPx0vr32v/JVuPV6zfdJYadgj/eFRyTNE4msd9kE82Wc46eU
-YiI+LuXZ3ZMUNWGY7MK2pOUUS52JsBQ3K235dA5WaU4x8OBlY/WkNYX/eLbNs5jj
-FzLmhZZ1
------END CERTIFICATE-----
diff --git a/tests/cts/hostside-network-policy/instrumentation_arguments/Android.bp b/tests/cts/hostside-network-policy/instrumentation_arguments/Android.bp
deleted file mode 100644
index cdede36..0000000
--- a/tests/cts/hostside-network-policy/instrumentation_arguments/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "ArgumentConstants",
- srcs: ["src/**/*.java"],
-}
diff --git a/tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java b/tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
deleted file mode 100644
index 0fe98e9..0000000
--- a/tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.netpolicy.arguments;
-
-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-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
deleted file mode 100644
index 422231d..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy;
-
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-
-import android.platform.test.annotations.FlakyTest;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
-import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
-
-import org.junit.Test;
-
-import java.util.Map;
-
-@FlakyTest(bugId = 288324467)
-public class HostsideConnOnActivityStartTest extends HostsideNetworkPolicyTestCase {
- private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
-
- @BeforeClassWithInfo
- public static void setUpOnce(TestInformation testInfo) throws Exception {
- uninstallPackage(testInfo, TEST_APP2_PKG, false);
- installPackage(testInfo, TEST_APP2_APK);
- }
-
- @AfterClassWithInfo
- public static void tearDownOnce(TestInformation testInfo) throws DeviceNotAvailableException {
- uninstallPackage(testInfo, TEST_APP2_PKG, true);
- }
-
- @Test
- public void testStartActivity_batterySaver() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
- }
-
- @Test
- public void testStartActivity_dataSaver() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
- }
-
- @FlakyTest(bugId = 231440256)
- @Test
- public void testStartActivity_doze() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
- }
-
- @Test
- public void testStartActivity_appStandby() throws Exception {
- 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 {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_default",
- Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
- }
-}
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
deleted file mode 100644
index 62952bb..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.netpolicy;
-
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-
-import android.platform.test.annotations.FlakyTest;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Map;
-
-// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side tests.
-@FlakyTest(bugId = 288324467)
-public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkPolicyTestCase {
- private static final String METERED_TEST_CLASS = TEST_PKG + ".DefaultRestrictionsMeteredTest";
- private static final String NON_METERED_TEST_CLASS =
- TEST_PKG + ".DefaultRestrictionsNonMeteredTest";
-
- @Before
- public void setUp() throws Exception {
- uninstallPackage(TEST_APP2_PKG, false);
- installPackage(TEST_APP2_APK);
- }
-
- @After
- public void tearDown() throws Exception {
- uninstallPackage(TEST_APP2_PKG, true);
- }
-
- private void runMeteredTest(String methodName) throws DeviceNotAvailableException {
- runDeviceTestsWithCustomOptions(TEST_PKG, METERED_TEST_CLASS, methodName,
- Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
- }
-
- private void runNonMeteredTest(String methodName) throws DeviceNotAvailableException {
- runDeviceTestsWithCustomOptions(TEST_PKG, NON_METERED_TEST_CLASS, methodName,
- Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
- }
-
- @Test
- public void testMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
- throws Exception {
- runMeteredTest("testActivityNetworkAccess");
- }
-
- @Test
- public void testMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
- throws Exception {
- runMeteredTest("testFgsNetworkAccess");
- }
-
- @Test
- public void testMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
- runMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
- }
-
- @Test
- public void testMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
- throws Exception {
- runMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
- }
-
- @Test
- public void testNonMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
- throws Exception {
- runNonMeteredTest("testActivityNetworkAccess");
- }
-
- @Test
- public void testNonMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
- throws Exception {
- runNonMeteredTest("testFgsNetworkAccess");
- }
-
- @Test
- public void testNonMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
- runNonMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
- }
-
- @Test
- public void testNonMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
- throws Exception {
- runNonMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
- }
-}
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
deleted file mode 100644
index 2c2b118..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy;
-
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-
-import android.platform.test.annotations.FlakyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Map;
-
-@FlakyTest(bugId = 288324467)
-public class HostsideNetworkCallbackTests extends HostsideNetworkPolicyTestCase {
-
- @Before
- public void setUp() throws Exception {
- uninstallPackage(TEST_APP2_PKG, false);
- installPackage(TEST_APP2_APK);
- }
-
- @After
- public void tearDown() throws Exception {
- uninstallPackage(TEST_APP2_PKG, true);
- }
-
- @Test
- public void testOnBlockedStatusChanged_dataSaver() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
- }
-
- @Test
- public void testOnBlockedStatusChanged_powerSaver() throws Exception {
- 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 {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkCallbackTest",
- "testOnBlockedStatusChanged_default", Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
- }
-}
-
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
deleted file mode 100644
index 8ffe360..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.netpolicy;
-
-import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Map;
-
-public class HostsideNetworkPolicyManagerTests extends HostsideNetworkPolicyTestCase {
- @Before
- public void setUp() throws Exception {
- uninstallPackage(TEST_APP2_PKG, false);
- installPackage(TEST_APP2_APK);
- }
-
- @After
- public void tearDown() throws Exception {
- uninstallPackage(TEST_APP2_PKG, true);
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withUidNotBlocked() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkPolicyManagerTest",
- "testIsUidNetworkingBlocked_withUidNotBlocked");
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidNetworkingBlocked_withSystemUid");
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkPolicyManagerTest",
- "testIsUidNetworkingBlocked_withDataSaverMode");
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withRestrictedNetworkingMode() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkPolicyManagerTest",
- "testIsUidNetworkingBlocked_withRestrictedNetworkingMode");
- }
-
- @Test
- public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG,
- TEST_PKG + ".NetworkPolicyManagerTest",
- "testIsUidNetworkingBlocked_withPowerSaverMode");
- }
-
- @Test
- public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
- 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 {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest",
- "testIsUidNetworkingBlocked_whenInBackground",
- Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
- }
-}
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
deleted file mode 100644
index 6de6b17..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.netpolicy;
-
-import static com.android.cts.netpolicy.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.tradefed.config.Option;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.targetprep.BuildError;
-import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.targetprep.suite.SuiteApkInstaller;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
-import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
-import com.android.tradefed.util.RunUtil;
-
-import org.junit.runner.RunWith;
-
-import java.util.Map;
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-abstract class HostsideNetworkPolicyTestCase extends BaseHostJUnit4Test {
- protected static final boolean DEBUG = false;
- protected static final String TAG = "HostsideNetworkPolicyTests";
- protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
- protected static final String TEST_APK = "CtsHostsideNetworkPolicyTestsApp.apk";
- protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
- protected static final String TEST_APP2_APK = "CtsHostsideNetworkPolicyTestsApp2.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 {
- uninstallPackage(testInfo, TEST_PKG, false);
- installPackage(testInfo, TEST_APK);
- }
-
- @AfterClassWithInfo
- public static void tearDownOnceBase(TestInformation testInfo)
- throws DeviceNotAvailableException {
- uninstallPackage(testInfo, TEST_PKG, true);
- }
-
- // Custom static method to install the specified package, this is used to bypass auto-cleanup
- // per test in BaseHostJUnit4.
- protected static void installPackage(TestInformation testInfo, String apk)
- throws DeviceNotAvailableException, TargetSetupError {
- assertNotNull(testInfo);
- final int userId = testInfo.getDevice().getCurrentUser();
- final SuiteApkInstaller installer = new SuiteApkInstaller();
- // Force the apk clean up
- installer.setCleanApk(true);
- installer.addTestFileName(apk);
- installer.setUserId(userId);
- installer.setShouldGrantPermission(true);
- installer.addInstallArg("-t");
- try {
- installer.setUp(testInfo);
- } catch (BuildError e) {
- throw new TargetSetupError(
- e.getMessage(), e, testInfo.getDevice().getDeviceDescriptor(), e.getErrorId());
- }
- }
-
- protected void installPackage(String apk) throws DeviceNotAvailableException, TargetSetupError {
- installPackage(getTestInformation(), apk);
- }
-
- protected static void uninstallPackage(TestInformation testInfo, String packageName,
- boolean shouldSucceed)
- throws DeviceNotAvailableException {
- assertNotNull(testInfo);
- final String result = testInfo.getDevice().uninstallPackage(packageName);
- if (shouldSucceed) {
- assertNull("uninstallPackage(" + packageName + ") failed: " + result, result);
- }
- }
-
- protected void uninstallPackage(String packageName,
- boolean shouldSucceed)
- throws DeviceNotAvailableException {
- uninstallPackage(getTestInformation(), packageName, shouldSucceed);
- }
-
- protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException {
- final String command = "cmd package list packages " + packageName;
- final int max_tries = 5;
- for (int i = 1; i <= max_tries; i++) {
- final String result = runCommand(command);
- if (result.trim().isEmpty()) {
- return;
- }
- // 'list packages' filters by substring, so we need to iterate with the results
- // and check one by one, otherwise 'com.android.cts.netpolicy.hostside' could return
- // 'com.android.cts.netpolicy.hostside.app2'
- boolean found = false;
- for (String line : result.split("[\\r\\n]+")) {
- if (line.endsWith(packageName)) {
- found = true;
- break;
- }
- }
- if (!found) {
- return;
- }
- Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
- + "); sleeping 1s before polling again");
- RunUtil.getDefault().sleep(1000);
- }
- fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
- }
-
- protected int getUid(String packageName) throws DeviceNotAvailableException {
- final int currentUser = getDevice().getCurrentUser();
- final String uidLines = runCommand(
- "cmd package list packages -U --user " + currentUser + " " + packageName);
- for (String uidLine : uidLines.split("\n")) {
- if (uidLine.startsWith("package:" + packageName + " uid:")) {
- final String[] uidLineParts = uidLine.split(":");
- // 3rd entry is package uid
- return Integer.parseInt(uidLineParts[2].trim());
- }
- }
- throw new IllegalStateException("Failed to find the test app on the device; pkg="
- + packageName + ", u=" + currentUser);
- }
-
- 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);
-
- // 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);
- }
-
- protected String runCommand(String command) throws DeviceNotAvailableException {
- Log.d(TAG, "Command: '" + command + "'");
- final String output = getDevice().executeShellCommand(command);
- if (DEBUG) Log.v(TAG, "Output: " + output.trim());
- return output;
- }
-}
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
deleted file mode 100644
index 0261c7d..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.netpolicy;
-
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.FlakyTest;
-import android.platform.test.annotations.SecurityTest;
-
-import com.android.ddmlib.Log;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.util.RunUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@FlakyTest(bugId = 288324467)
-public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkPolicyTestCase {
-
- @Before
- public void setUp() throws Exception {
- uninstallPackage(TEST_APP2_PKG, false);
- installPackage(TEST_APP2_APK);
- }
-
- @After
- public void tearDown() throws Exception {
- uninstallPackage(TEST_APP2_PKG, true);
- }
-
- @SecurityTest
- @Test
- public void testDataWarningReceiver() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataWarningReceiverTest",
- "testSnoozeWarningNotReceived");
- }
-
- /**************************
- * Data Saver Mode tests. *
- **************************/
-
- @Test
- public void testDataSaverMode_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testGetRestrictBackgroundStatus_disabled");
- }
-
- @Test
- public void testDataSaverMode_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testGetRestrictBackgroundStatus_whitelisted");
- }
-
- @Test
- public void testDataSaverMode_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testGetRestrictBackgroundStatus_enabled");
- }
-
- @Test
- public void testDataSaverMode_blacklisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testGetRestrictBackgroundStatus_blacklisted");
- }
-
- @Test
- public void testDataSaverMode_reinstall() throws Exception {
- final int oldUid = getUid(TEST_APP2_PKG);
-
- // Make sure whitelist is revoked when package is removed
- addRestrictBackgroundWhitelist(oldUid);
-
- uninstallPackage(TEST_APP2_PKG, true);
- assertPackageUninstalled(TEST_APP2_PKG);
- assertRestrictBackgroundWhitelist(oldUid, false);
-
- installPackage(TEST_APP2_APK);
- final int newUid = getUid(TEST_APP2_PKG);
- assertRestrictBackgroundWhitelist(oldUid, false);
- assertRestrictBackgroundWhitelist(newUid, false);
- }
-
- @Test
- public void testDataSaverMode_requiredWhitelistedPackages() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
- }
-
- @Test
- public void testDataSaverMode_broadcastNotSentOnUnsupportedDevices() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
- "testBroadcastNotSentOnUnsupportedDevices");
- }
-
- /*****************************
- * Battery Saver Mode tests. *
- *****************************/
-
- @Test
- public void testBatterySaverModeMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
- @Test
- public void testBatterySaverModeMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testBatterySaverModeMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- @Test
- public void testBatterySaverMode_reinstall() throws Exception {
- if (!isDozeModeEnabled()) {
- Log.w(TAG, "testBatterySaverMode_reinstall() skipped because device does not support "
- + "Doze Mode");
- return;
- }
-
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
-
- uninstallPackage(TEST_APP2_PKG, true);
- assertPackageUninstalled(TEST_APP2_PKG);
- assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
-
- installPackage(TEST_APP2_APK);
- assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
- }
-
- @Test
- public void testBatterySaverModeNonMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
- @Test
- public void testBatterySaverModeNonMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testBatterySaverModeNonMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- /*******************
- * App idle tests. *
- *******************/
-
- @Test
- public void testAppIdleMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
- @Test
- public void testAppIdleMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testAppIdleMetered_tempWhitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
- "testBackgroundNetworkAccess_tempWhitelisted");
- }
-
- @Test
- public void testAppIdleMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- @Test
- public void testAppIdleMetered_idleWhitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
- "testAppIdleNetworkAccess_idleWhitelisted");
- }
-
- // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
- // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
- // public void testAppIdle_reinstall() throws Exception {
- // }
-
- @Test
- public void testAppIdleNonMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
-
- @Test
- public void testAppIdleNonMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testBackgroundNetworkAccess_tempWhitelisted");
- }
-
- @Test
- public void testAppIdleNonMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- @Test
- public void testAppIdleNonMetered_idleWhitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testAppIdleNetworkAccess_idleWhitelisted");
- }
-
- @Test
- public void testAppIdleNonMetered_whenCharging() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testAppIdleNetworkAccess_whenCharging");
- }
-
- @Test
- public void testAppIdleMetered_whenCharging() throws Exception {
- 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
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
- "testAppIdle_toast");
- }
-
- /********************
- * Doze Mode tests. *
- ********************/
-
- @Test
- public void testDozeModeMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
- @Test
- public void testDozeModeMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testDozeModeMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- @Test
- public void testDozeModeMetered_enabledButWhitelistedOnNotificationAction() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
- "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
- }
-
- // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
- // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
- // public void testDozeMode_reinstall() throws Exception {
- // }
-
- @Test
- public void testDozeModeNonMetered_disabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
- "testBackgroundNetworkAccess_disabled");
- }
-
- @Test
- public void testDozeModeNonMetered_whitelisted() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
- "testBackgroundNetworkAccess_whitelisted");
- }
-
- @Test
- public void testDozeModeNonMetered_enabled() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
- "testBackgroundNetworkAccess_enabled");
- }
-
- @Test
- public void testDozeModeNonMetered_enabledButWhitelistedOnNotificationAction()
- throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
- "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
- }
-
- /**********************
- * Mixed modes tests. *
- **********************/
-
- @Test
- public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testDataAndBatterySaverModes_meteredNetwork");
- }
-
- @Test
- public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testDataAndBatterySaverModes_nonMeteredNetwork");
- }
-
- @Test
- public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testDozeAndBatterySaverMode_powerSaveWhitelists");
- }
-
- @Test
- public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testDozeAndAppIdle_powerSaveWhitelists");
- }
-
- @Test
- public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testAppIdleAndDoze_tempPowerSaveWhitelists");
- }
-
- @Test
- public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
- }
-
- @Test
- public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testDozeAndAppIdle_appIdleWhitelist");
- }
-
- @Test
- public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists");
- }
-
- @Test
- public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
- "testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists");
- }
-
- /**************************
- * Restricted mode tests. *
- **************************/
-
- @Test
- public void testNetworkAccess_restrictedMode() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
- "testNetworkAccess");
- }
-
- @Test
- public void testNetworkAccess_restrictedMode_withBatterySaver() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
- "testNetworkAccess_withBatterySaver");
- }
-
- /************************
- * Expedited job tests. *
- ************************/
-
- @Test
- public void testMeteredNetworkAccess_expeditedJob() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
- }
-
- @Test
- public void testNonMeteredNetworkAccess_expeditedJob() throws Exception {
- runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
- }
-
- /*******************
- * Helper methods. *
- *******************/
-
- private void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
- final int max_tries = 5;
- boolean actual = false;
- for (int i = 1; i <= max_tries; i++) {
- final String output = runCommand("cmd netpolicy list restrict-background-whitelist ");
- actual = output.contains(Integer.toString(uid));
- if (expected == actual) {
- return;
- }
- Log.v(TAG, "whitelist check for uid " + uid + " doesn't match yet (expected "
- + expected + ", got " + actual + "); sleeping 1s before polling again");
- RunUtil.getDefault().sleep(1000);
- }
- fail("whitelist check for uid " + uid + " failed: expected "
- + expected + ", got " + actual);
- }
-
- private void assertPowerSaveModeWhitelist(String packageName, boolean expected)
- throws Exception {
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- assertDelayedCommand("dumpsys deviceidle whitelist =" + packageName,
- Boolean.toString(expected));
- }
-
- /**
- * Asserts the result of a command, wait and re-running it a couple times if necessary.
- */
- private void assertDelayedCommand(String command, String expectedResult)
- throws InterruptedException, DeviceNotAvailableException {
- final int maxTries = 5;
- for (int i = 1; i <= maxTries; i++) {
- final String result = runCommand(command).trim();
- if (result.equals(expectedResult)) return;
- Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
- + expectedResult + "' on attempt #; sleeping 1s before polling again");
- RunUtil.getDefault().sleep(1000);
- }
- fail("Command '" + command + "' did not return '" + expectedResult + "' after " + maxTries
- + " attempts");
- }
-
- protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
- runCommand("cmd netpolicy add restrict-background-whitelist " + uid);
- assertRestrictBackgroundWhitelist(uid, true);
- }
-
- private void addPowerSaveModeWhitelist(String packageName) throws Exception {
- Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
- // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
- // need to use netpolicy for whitelisting
- runCommand("dumpsys deviceidle whitelist +" + packageName);
- assertPowerSaveModeWhitelist(packageName, true);
- }
-
- protected boolean isDozeModeEnabled() throws Exception {
- final String result = runCommand("cmd deviceidle enabled deep").trim();
- return result.equals("1");
- }
-}
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
deleted file mode 100644
index cbf2f4d..0000000
--- a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.netpolicy;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.log.LogUtil;
-import com.android.tradefed.targetprep.ITargetPreparer;
-
-public class NetworkPolicyTestsPreparer implements ITargetPreparer {
- private ITestDevice mDevice;
- private boolean mOriginalAirplaneModeEnabled;
- private String mOriginalAppStandbyEnabled;
- private String mOriginalBatteryStatsConstants;
- private final static String KEY_STABLE_CHARGING_DELAY_MS = "battery_charged_delay_ms";
- private final static int DESIRED_STABLE_CHARGING_DELAY_MS = 0;
-
- @Override
- public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
- mDevice = testInformation.getDevice();
- mOriginalAppStandbyEnabled = getAppStandbyEnabled();
- setAppStandbyEnabled("1");
- LogUtil.CLog.d("Original app_standby_enabled: " + mOriginalAppStandbyEnabled);
-
- mOriginalBatteryStatsConstants = getBatteryStatsConstants();
- setBatteryStatsConstants(
- KEY_STABLE_CHARGING_DELAY_MS + "=" + DESIRED_STABLE_CHARGING_DELAY_MS);
- LogUtil.CLog.d("Original battery_saver_constants: " + mOriginalBatteryStatsConstants);
-
- mOriginalAirplaneModeEnabled = getAirplaneModeEnabled();
- // Turn off airplane mode in case another test left the device in that state.
- setAirplaneModeEnabled(false);
- LogUtil.CLog.d("Original airplane mode state: " + mOriginalAirplaneModeEnabled);
- }
-
- @Override
- public void tearDown(TestInformation testInformation, Throwable e)
- throws DeviceNotAvailableException {
- setAirplaneModeEnabled(mOriginalAirplaneModeEnabled);
- setAppStandbyEnabled(mOriginalAppStandbyEnabled);
- setBatteryStatsConstants(mOriginalBatteryStatsConstants);
- }
-
- private void setAirplaneModeEnabled(boolean enable) throws DeviceNotAvailableException {
- executeCmd("cmd connectivity airplane-mode " + (enable ? "enable" : "disable"));
- }
-
- private boolean getAirplaneModeEnabled() throws DeviceNotAvailableException {
- return "enabled".equals(executeCmd("cmd connectivity airplane-mode").trim());
- }
-
- private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
- if ("null".equals(appStandbyEnabled)) {
- executeCmd("settings delete global app_standby_enabled");
- } else {
- executeCmd("settings put global app_standby_enabled " + appStandbyEnabled);
- }
- }
-
- private String getAppStandbyEnabled() throws DeviceNotAvailableException {
- return executeCmd("settings get global app_standby_enabled").trim();
- }
-
- private void setBatteryStatsConstants(String batteryStatsConstants)
- throws DeviceNotAvailableException {
- executeCmd("settings put global battery_stats_constants \"" + batteryStatsConstants + "\"");
- }
-
- private String getBatteryStatsConstants() throws DeviceNotAvailableException {
- return executeCmd("settings get global battery_stats_constants");
- }
-
- private String executeCmd(String cmd) throws DeviceNotAvailableException {
- final String output = mDevice.executeShellCommand(cmd).trim();
- LogUtil.CLog.d("Output for '%s': %s", cmd, output);
- return output;
- }
-}
diff --git a/tests/cts/multidevices/Android.bp b/tests/cts/multidevices/Android.bp
index 1d30d68..dc90adb 100644
--- a/tests/cts/multidevices/Android.bp
+++ b/tests/cts/multidevices/Android.bp
@@ -22,10 +22,10 @@
main: "connectivity_multi_devices_test.py",
srcs: [
"connectivity_multi_devices_test.py",
- "utils/*.py",
],
libs: [
"mobly",
+ "net-tests-utils-host-python-common",
],
test_suites: [
"cts",
diff --git a/tests/cts/multidevices/connectivity_multi_devices_test.py b/tests/cts/multidevices/connectivity_multi_devices_test.py
index abd6fe2..7e7bbf5 100644
--- a/tests/cts/multidevices/connectivity_multi_devices_test.py
+++ b/tests/cts/multidevices/connectivity_multi_devices_test.py
@@ -1,15 +1,17 @@
# Lint as: python3
"""Connectivity multi devices tests."""
import sys
+
+from mobly import asserts
from mobly import base_test
from mobly import test_runner
from mobly import utils
from mobly.controllers import android_device
-from utils import mdns_utils
-from utils import tether_utils
-from utils.tether_utils import UpstreamType
+from net_tests_utils.host.python import adb_utils, apf_utils, assert_utils, mdns_utils, tether_utils
+from net_tests_utils.host.python.tether_utils import UpstreamType
CONNECTIVITY_MULTI_DEVICES_SNIPPET_PACKAGE = "com.google.snippet.connectivity"
+COUNTER_DROPPED_ETHERTYPE_NOT_ALLOWED = "DROPPED_ETHERTYPE_NOT_ALLOWED"
class ConnectivityMultiDevicesTest(base_test.BaseTestClass):
@@ -66,22 +68,67 @@
tether_utils.assume_hotspot_test_preconditions(
self.serverDevice, self.clientDevice, UpstreamType.NONE
)
+ mdns_utils.assume_mdns_test_preconditions(
+ self.clientDevice, self.serverDevice
+ )
try:
# Connectivity of the client verified by asserting the validated capability.
tether_utils.setup_hotspot_and_client_for_upstream_type(
- self.serverDevice, self.clientDevice, UpstreamType.NONE
+ self.serverDevice, self.clientDevice, UpstreamType.NONE
)
mdns_utils.register_mdns_service_and_discover_resolve(
- self.clientDevice, self.serverDevice
+ self.clientDevice, self.serverDevice
)
finally:
- mdns_utils.cleanup_mdns_service(
- self.clientDevice, self.serverDevice
- )
+ mdns_utils.cleanup_mdns_service(self.clientDevice, self.serverDevice)
tether_utils.cleanup_tethering_for_upstream_type(
- self.serverDevice, UpstreamType.NONE
+ self.serverDevice, UpstreamType.NONE
)
+ def test_apf_drop_ethercat(self):
+ tether_utils.assume_hotspot_test_preconditions(
+ self.serverDevice, self.clientDevice, UpstreamType.NONE
+ )
+ client = self.clientDevice.connectivity_multi_devices_snippet
+ try:
+ server_iface_name, client_network = (
+ tether_utils.setup_hotspot_and_client_for_upstream_type(
+ self.serverDevice, self.clientDevice, UpstreamType.NONE
+ )
+ )
+ client_iface_name = client.getInterfaceNameFromNetworkHandle(client_network)
+
+ adb_utils.set_doze_mode(self.clientDevice, True)
+
+ count_before_test = apf_utils.get_apf_counter(
+ self.clientDevice,
+ client_iface_name,
+ COUNTER_DROPPED_ETHERTYPE_NOT_ALLOWED,
+ )
+ try:
+ apf_utils.send_broadcast_empty_ethercat_packet(
+ self.serverDevice, server_iface_name
+ )
+ except apf_utils.UnsupportedOperationException:
+ asserts.skip(
+ "NetworkStack is too old to support send raw packet, skip test."
+ )
+
+ assert_utils.expect_with_retry(
+ lambda: apf_utils.get_apf_counter(
+ self.clientDevice,
+ client_iface_name,
+ COUNTER_DROPPED_ETHERTYPE_NOT_ALLOWED,
+ )
+ > count_before_test
+ )
+ finally:
+ adb_utils.set_doze_mode(self.clientDevice, False)
+ tether_utils.cleanup_tethering_for_upstream_type(
+ self.serverDevice, UpstreamType.NONE
+ )
+
+
if __name__ == "__main__":
# Take test args
if "--" in sys.argv:
diff --git a/tests/cts/multidevices/snippet/Android.bp b/tests/cts/multidevices/snippet/Android.bp
index b0b32c2..c94087e 100644
--- a/tests/cts/multidevices/snippet/Android.bp
+++ b/tests/cts/multidevices/snippet/Android.bp
@@ -26,6 +26,7 @@
srcs: [
"ConnectivityMultiDevicesSnippet.kt",
"MdnsMultiDevicesSnippet.kt",
+ "Wifip2pMultiDevicesSnippet.kt",
],
manifest: "AndroidManifest.xml",
static_libs: [
diff --git a/tests/cts/multidevices/snippet/AndroidManifest.xml b/tests/cts/multidevices/snippet/AndroidManifest.xml
index 967e581..4637497 100644
--- a/tests/cts/multidevices/snippet/AndroidManifest.xml
+++ b/tests/cts/multidevices/snippet/AndroidManifest.xml
@@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
+ android:usesPermissionFlags="neverForLocation" />
<application>
<!-- Add any classes that implement the Snippet interface as meta-data, whose
value is a comma-separated string, each section being the package path
@@ -28,7 +30,8 @@
<meta-data
android:name="mobly-snippets"
android:value="com.google.snippet.connectivity.ConnectivityMultiDevicesSnippet,
- com.google.snippet.connectivity.MdnsMultiDevicesSnippet" />
+ com.google.snippet.connectivity.MdnsMultiDevicesSnippet,
+ com.google.snippet.connectivity.Wifip2pMultiDevicesSnippet" />
</application>
<!-- Add an instrumentation tag so that the app can be launched through an
instrument command. The runner `com.google.android.mobly.snippet.SnippetRunner`
diff --git a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
index f4ad2c4..7368669 100644
--- a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
@@ -21,6 +21,7 @@
import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.content.pm.PackageManager.FEATURE_WIFI
import android.net.ConnectivityManager
+import android.net.Network
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkRequest
@@ -35,6 +36,7 @@
import android.net.wifi.WifiNetworkSpecifier
import android.net.wifi.WifiSsid
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.modules.utils.build.SdkLevel
import com.android.testutils.AutoReleaseNetworkCallbackRule
import com.android.testutils.ConnectUtil
import com.android.testutils.NetworkCallbackHelper
@@ -70,6 +72,9 @@
@Rpc(description = "Check whether the device supporters AP + STA concurrency.")
fun isStaApConcurrencySupported() = wifiManager.isStaApConcurrencySupported()
+ @Rpc(description = "Check whether the device SDK is as least T")
+ fun isAtLeastT() = SdkLevel.isAtLeastT()
+
@Rpc(description = "Request cellular connection and ensure it is the default network.")
fun requestCellularAndEnsureDefault() {
ctsNetUtils.disableWifi()
@@ -129,6 +134,12 @@
}
}
+ @Rpc(description = "Get interface name from NetworkHandle")
+ fun getInterfaceNameFromNetworkHandle(networkHandle: Long): String {
+ val network = Network.fromNetworkHandle(networkHandle)
+ return cm.getLinkProperties(network)!!.getInterfaceName()!!
+ }
+
@Rpc(description = "Check whether the device supports hotspot feature.")
fun hasHotspotFeature(): Boolean {
val tetheringCallback = ctsTetheringUtils.registerTetheringEventCallback()
@@ -140,7 +151,7 @@
}
@Rpc(description = "Start a hotspot with given SSID and passphrase.")
- fun startHotspot(ssid: String, passphrase: String) {
+ fun startHotspot(ssid: String, passphrase: String): String {
// Store old config.
runAsShell(OVERRIDE_WIFI_CONFIG) {
oldSoftApConfig = wifiManager.getSoftApConfiguration()
@@ -157,7 +168,7 @@
val tetheringCallback = ctsTetheringUtils.registerTetheringEventCallback()
try {
tetheringCallback.expectNoTetheringActive()
- ctsTetheringUtils.startWifiTethering(tetheringCallback)
+ return ctsTetheringUtils.startWifiTethering(tetheringCallback).getInterface()
} finally {
ctsTetheringUtils.unregisterTetheringEventCallback(tetheringCallback)
}
diff --git a/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt
new file mode 100644
index 0000000..e0929bb
--- /dev/null
+++ b/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.google.snippet.connectivity
+
+import android.net.wifi.WifiManager
+import android.net.wifi.p2p.WifiP2pManager
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.android.mobly.snippet.Snippet
+import com.google.android.mobly.snippet.rpc.Rpc
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import kotlin.test.fail
+
+private const val TIMEOUT_MS = 60000L
+
+class Wifip2pMultiDevicesSnippet : Snippet {
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().getTargetContext() }
+ private val wifiManager by lazy {
+ context.getSystemService(WifiManager::class.java)
+ ?: fail("Could not get WifiManager service")
+ }
+ private val wifip2pManager by lazy {
+ context.getSystemService(WifiP2pManager::class.java)
+ ?: fail("Could not get WifiP2pManager service")
+ }
+ private lateinit var wifip2pChannel: WifiP2pManager.Channel
+
+ @Rpc(description = "Check whether the device supports Wi-Fi P2P.")
+ fun isP2pSupported() = wifiManager.isP2pSupported()
+
+ @Rpc(description = "Start Wi-Fi P2P")
+ fun startWifiP2p() {
+ // Initialize Wi-Fi P2P
+ wifip2pChannel = wifip2pManager.initialize(context, context.mainLooper, null)
+
+ // Ensure the Wi-Fi P2P channel is available
+ val p2pStateEnabledFuture = CompletableFuture<Boolean>()
+ wifip2pManager.requestP2pState(wifip2pChannel) { state ->
+ if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
+ p2pStateEnabledFuture.complete(true)
+ }
+ }
+ p2pStateEnabledFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ @Rpc(description = "Stop Wi-Fi P2P")
+ fun stopWifiP2p() {
+ if (this::wifip2pChannel.isInitialized) {
+ wifip2pManager.cancelConnect(wifip2pChannel, null)
+ wifip2pManager.removeGroup(wifip2pChannel, null)
+ }
+ }
+}
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index ae85701..1cd8327 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -111,25 +111,6 @@
min_sdk_version: "30",
}
-// Networking CTS tests that target the latest released SDK. These tests can be installed on release
-// devices at any point in the Android release cycle and are useful for qualifying mainline modules
-// on release devices.
-android_test {
- name: "CtsNetTestCasesLatestSdk",
- defaults: [
- "ConnectivityTestsLatestSdkDefaults",
- "CtsNetTestCasesDefaults",
- "CtsNetTestCasesApiStableDefaults",
- ],
- test_suites: [
- "general-tests",
- "mts-dnsresolver",
- "mts-networking",
- "mts-tethering",
- "mts-wifi",
- ],
-}
-
java_defaults {
name: "CtsNetTestCasesMaxTargetSdkDefaults",
defaults: [
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 077c3ef..24431a6 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -25,6 +25,7 @@
<option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.tethering.apex" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="has-server-side-config" value="false" />
<option name="target" value="device" />
<option name="config-filename" value="{MODULE}" />
<option name="version" value="1.0" />
@@ -48,8 +49,8 @@
those tests with an annotation matching the name of the APK.
This allows us to maintain one AndroidTestTemplate.xml for all CtsNetTestCases*.apk,
- and have CtsNetTestCases and CtsNetTestCasesLatestSdk run all tests, but have
- CtsNetTestCasesMaxTargetSdk31 run only tests that require target SDK 31.
+ and have CtsNetTestCases run all tests, but have CtsNetTestCasesMaxTargetSdk31 run only
+ tests that require target SDK 31.
This relies on the fact that if the class specified in include-annotation exists, then
the runner will only run the tests annotated with that annotation, but if it does not,
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index f6cbeeb..9ac2c67 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -27,10 +27,17 @@
import android.net.NetworkRequest
import android.net.apf.ApfCapabilities
import android.net.apf.ApfConstants.ETH_ETHERTYPE_OFFSET
+import android.net.apf.ApfConstants.ETH_HEADER_LEN
+import android.net.apf.ApfConstants.ICMP6_CHECKSUM_OFFSET
import android.net.apf.ApfConstants.ICMP6_TYPE_OFFSET
+import android.net.apf.ApfConstants.IPV6_DEST_ADDR_OFFSET
+import android.net.apf.ApfConstants.IPV6_HEADER_LEN
import android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET
+import android.net.apf.ApfConstants.IPV6_SRC_ADDR_OFFSET
import android.net.apf.ApfCounterTracker
+import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_MULTICAST_PING
import android.net.apf.ApfCounterTracker.Counter.FILTER_AGE_16384THS
+import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_ICMP
import android.net.apf.ApfV4Generator
import android.net.apf.ApfV4GeneratorBase
import android.net.apf.ApfV6Generator
@@ -61,6 +68,12 @@
import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
import com.android.compatibility.common.util.VsrTest
import com.android.internal.util.HexDump
+import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN
+import com.android.net.module.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET
+import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
+import com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET
+import com.android.net.module.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN
+import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN
import com.android.net.module.util.PacketReader
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -216,8 +229,8 @@
Os.sendto(sockFd!!, packet, 0, packet.size, 0, PING_DESTINATION)
}
- fun expectPingReply(): ByteArray {
- return futureReply!!.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ fun expectPingReply(timeoutMs: Long = TIMEOUT_MS): ByteArray {
+ return futureReply!!.get(timeoutMs, TimeUnit.MILLISECONDS)
}
fun expectPingDropped() {
@@ -397,6 +410,12 @@
}
}
+ private fun installAndVerifyProgram(program: ByteArray) {
+ installProgram(program)
+ val readResult = readProgram().take(program.size).toByteArray()
+ assertThat(readResult).isEqualTo(program)
+ }
+
fun ApfV4GeneratorBase<*>.addPassIfNotIcmpv6EchoReply() {
// If not IPv6 -> PASS
addLoad16(R0, ETH_ETHERTYPE_OFFSET)
@@ -423,12 +442,7 @@
assumeApfVersionSupportAtLeast(4)
// clear any active APF filter
- var gen = ApfV4Generator(
- caps.apfVersionSupported,
- caps.maximumApfProgramSize,
- caps.maximumApfProgramSize
- ).addPass()
- installProgram(gen.generate())
+ clearApfMemory()
readProgram() // wait for install completion
// Assert that initial ping does not get filtered.
@@ -442,7 +456,7 @@
assertThat(packetReader.expectPingReply()).isEqualTo(data)
// Generate an APF program that drops the next ping
- gen = ApfV4Generator(
+ val gen = ApfV4Generator(
caps.apfVersionSupported,
caps.maximumApfProgramSize,
caps.maximumApfProgramSize
@@ -459,8 +473,7 @@
gen.addJump(BaseApfGenerator.DROP_LABEL)
val program = gen.generate()
- installProgram(program)
- readProgram() // wait for install completion
+ installAndVerifyProgram(program)
packetReader.sendPing(data, payloadSize)
packetReader.expectPingDropped()
@@ -505,8 +518,7 @@
val program = gen.generate()
assertThat(program.size).isLessThan(counterRegion)
- installProgram(program)
- readProgram() // wait for install completion
+ installAndVerifyProgram(program)
// Trigger the program by sending a ping and waiting on the reply.
val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
@@ -554,8 +566,7 @@
gen.addLoadFromMemory(R0, MemorySlot.FILTER_AGE_SECONDS)
gen.addStoreData(R0, 0)
- installProgram(gen.generate())
- readProgram() // wait for install completion
+ installAndVerifyProgram(gen.generate())
val payloadSize = 56
val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
@@ -595,8 +606,7 @@
gen.addLoadFromMemory(R0, MemorySlot.FILTER_AGE_16384THS)
gen.addStoreCounter(FILTER_AGE_16384THS, R0)
- installProgram(gen.generate())
- readProgram() // wait for install completion
+ installAndVerifyProgram(gen.generate())
val payloadSize = 56
val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
@@ -622,4 +632,102 @@
assertThat(timeDiff).isGreaterThan(timeDiffLowerBound)
assertThat(timeDiff).isLessThan(timeDiffUpperBound)
}
+
+ @VsrTest(
+ requirements = ["VSR-5.3.12-002", "VSR-5.3.12-005", "VSR-5.3.12-012", "VSR-5.3.12-013",
+ "VSR-5.3.12-014", "VSR-5.3.12-015", "VSR-5.3.12-016", "VSR-5.3.12-017"]
+ )
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testReplyPing() {
+ assumeApfVersionSupportAtLeast(6000)
+ installProgram(ByteArray(caps.maximumApfProgramSize) { 0 }) // Clear previous program
+ readProgram() // Ensure installation is complete
+
+ val payloadSize = 56
+ val payload = ByteArray(payloadSize).also { Random.nextBytes(it) }
+ val firstByte = payload.take(1).toByteArray()
+
+ val pingRequestIpv6PayloadLen = PING_HEADER_LENGTH + 1
+ val pingRequestPktLen = ETH_HEADER_LEN + IPV6_HEADER_LEN + pingRequestIpv6PayloadLen
+
+ val gen = ApfV6Generator(
+ caps.apfVersionSupported,
+ caps.maximumApfProgramSize,
+ caps.maximumApfProgramSize
+ )
+ val skipPacketLabel = gen.uniqueLabel
+
+ // Summary of the program:
+ // if the packet is not ICMPv6 echo reply
+ // pass
+ // else if the echo reply payload size is 1
+ // increase PASSED_IPV6_ICMP counter
+ // pass
+ // else
+ // transmit a ICMPv6 echo request packet with the first byte of the payload in the reply
+ // increase DROPPED_IPV6_MULTICAST_PING counter
+ // drop
+ val program = gen
+ .addLoad16(R0, ETH_ETHERTYPE_OFFSET)
+ .addJumpIfR0NotEquals(ETH_P_IPV6.toLong(), skipPacketLabel)
+ .addLoad8(R0, IPV6_NEXT_HEADER_OFFSET)
+ .addJumpIfR0NotEquals(IPPROTO_ICMPV6.toLong(), skipPacketLabel)
+ .addLoad8(R0, ICMP6_TYPE_OFFSET)
+ .addJumpIfR0NotEquals(0x81, skipPacketLabel) // Echo reply type
+ .addLoadFromMemory(R0, MemorySlot.PACKET_SIZE)
+ .addCountAndPassIfR0Equals(
+ (ETHER_HEADER_LEN + IPV6_HEADER_LEN + PING_HEADER_LENGTH + firstByte.size)
+ .toLong(),
+ PASSED_IPV6_ICMP
+ )
+ // Ping Packet Generation
+ .addAllocate(pingRequestPktLen)
+ // Eth header
+ .addPacketCopy(ETHER_SRC_ADDR_OFFSET, ETHER_ADDR_LEN) // dst MAC address
+ .addPacketCopy(ETHER_DST_ADDR_OFFSET, ETHER_ADDR_LEN) // src MAC address
+ .addWriteU16(ETH_P_IPV6) // IPv6 type
+ // IPv6 Header
+ .addWrite32(0x60000000) // IPv6 Header: version, traffic class, flowlabel
+ // payload length (2 bytes) | next header: ICMPv6 (1 byte) | hop limit (1 byte)
+ .addWrite32(pingRequestIpv6PayloadLen shl 16 or (IPPROTO_ICMPV6 shl 8 or 64))
+ .addPacketCopy(IPV6_DEST_ADDR_OFFSET, IPV6_ADDR_LEN) // src ip
+ .addPacketCopy(IPV6_SRC_ADDR_OFFSET, IPV6_ADDR_LEN) // dst ip
+ // ICMPv6
+ .addWriteU8(0x80) // type: echo request
+ .addWriteU8(0) // code
+ .addWriteU16(pingRequestIpv6PayloadLen) // checksum
+ // identifier
+ .addPacketCopy(ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_MIN_LEN, 2)
+ .addWriteU16(0) // sequence number
+ .addDataCopy(firstByte) // data
+ .addTransmitL4(
+ ETHER_HEADER_LEN, // ip_ofs
+ ICMP6_CHECKSUM_OFFSET, // csum_ofs
+ IPV6_SRC_ADDR_OFFSET, // csum_start
+ IPPROTO_ICMPV6, // partial_sum
+ false // udp
+ )
+ // Warning: the program abuse DROPPED_IPV6_MULTICAST_PING for debugging purpose
+ .addCountAndDrop(DROPPED_IPV6_MULTICAST_PING)
+ .defineLabel(skipPacketLabel)
+ .addPass()
+ .generate()
+
+ installAndVerifyProgram(program)
+
+ packetReader.sendPing(payload, payloadSize)
+
+ val replyPayload = try {
+ packetReader.expectPingReply(TIMEOUT_MS * 2)
+ } catch (e: TimeoutException) {
+ byteArrayOf() // Empty payload if timeout occurs
+ }
+
+ val apfCounterTracker = ApfCounterTracker()
+ apfCounterTracker.updateCountersFromData(readProgram())
+ Log.i(TAG, "counter map: ${apfCounterTracker.counters}")
+
+ assertThat(replyPayload).isEqualTo(firstByte)
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 21eb90f..9458460 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -195,7 +195,6 @@
import androidx.test.filters.RequiresDevice;
import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.DynamicConfigDeviceSide;
import com.android.internal.util.ArrayUtils;
@@ -211,6 +210,7 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.DeviceConfigRule;
import com.android.testutils.DeviceInfoUtils;
import com.android.testutils.DumpTestUtils;
@@ -274,7 +274,8 @@
import fi.iki.elonen.NanoHTTPD.Response.IStatus;
import fi.iki.elonen.NanoHTTPD.Response.Status;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRunner.RestoreDefaultNetwork
public class ConnectivityManagerTest {
@Rule(order = 1)
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 752891f..fa44ae9 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -852,13 +852,14 @@
}
public void doTestContinuousQueries(Executor executor) throws InterruptedException {
- final String msg = "Test continuous " + QUERY_TIMES + " queries " + TEST_DOMAIN;
for (Network network : getTestableNetworks()) {
for (int i = 0; i < QUERY_TIMES ; ++i) {
- final VerifyCancelInetAddressCallback callback =
- new VerifyCancelInetAddressCallback(msg, null);
// query v6/v4 in turn
boolean queryV6 = (i % 2 == 0);
+ final String msg = "Test continuous " + QUERY_TIMES + " queries " + TEST_DOMAIN
+ + " on " + network + ", queryV6=" + queryV6;
+ final VerifyCancelInetAddressCallback callback =
+ new VerifyCancelInetAddressCallback(msg, null);
mDns.query(network, TEST_DOMAIN, queryV6 ? TYPE_AAAA : TYPE_A,
FLAG_NO_CACHE_LOOKUP, executor, null, callback);
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 06a827b..2c7d5c6 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -40,10 +40,10 @@
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.DevSdkIgnoreRunner;
import com.android.testutils.DeviceConfigRule;
import org.junit.Before;
@@ -53,7 +53,8 @@
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
+@DevSdkIgnoreRunner.RestoreDefaultNetwork
+@RunWith(DevSdkIgnoreRunner.class)
public class MultinetworkApiTest {
@Rule(order = 1)
public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule();
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index beb9274..60081d4 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -194,15 +194,16 @@
// TODO : enable this in a Mainline update or in V.
private const val SHOULD_CREATE_NETWORKS_IMMEDIATELY = false
-@RunWith(DevSdkIgnoreRunner::class)
-// NetworkAgent is not updatable in R-, so this test does not need to be compatible with older
-// versions. NetworkAgent was also based on AsyncChannel before S so cannot be tested the same way.
-@IgnoreUpTo(Build.VERSION_CODES.R)
+@AppModeFull(reason = "Instant apps can't use NetworkAgent because it needs NETWORK_FACTORY'.")
// NetworkAgent is updated as part of the connectivity module, and running NetworkAgent tests in MTS
// for modules other than Connectivity does not provide much value. Only run them in connectivity
// module MTS, so the tests only need to cover the case of an updated NetworkAgent.
@ConnectivityModuleTest
-@AppModeFull(reason = "Instant apps can't use NetworkAgent because it needs NETWORK_FACTORY'.")
+@DevSdkIgnoreRunner.RestoreDefaultNetwork
+// NetworkAgent is not updatable in R-, so this test does not need to be compatible with older
+// versions. NetworkAgent was also based on AsyncChannel before S so cannot be tested the same way.
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@RunWith(DevSdkIgnoreRunner::class)
class NetworkAgentTest {
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2")
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
index 284fcae..f45f881 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
@@ -59,7 +59,6 @@
@After
override fun tearDown() {
super.tearDown()
- setIncludeTestInterfaces(false)
}
@Test
@@ -107,7 +106,6 @@
@Test
fun testMdnsDiscoveryWorkOnTetheringInterface() {
assumeFalse(isInterfaceForTetheringAvailable())
- setIncludeTestInterfaces(true)
var downstreamIface: TestNetworkInterface? = null
var tetheringEventCallback: MyTetheringEventCallback? = null
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 81608f7..8794847 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -235,6 +235,12 @@
assertEquals(TETHERING_USB, tr2.getTetheringType());
assertTrue(tr2.isExemptFromEntitlementCheck());
assertFalse(tr2.getShouldShowEntitlementUi());
+
+ final TetheringRequest tr3 = new TetheringRequest.Builder(TETHERING_USB)
+ .setStaticIpv4Addresses(localAddr, clientAddr)
+ .setExemptFromEntitlementCheck(true)
+ .setShouldShowEntitlementUi(false).build();
+ assertEquals(tr2, tr3);
}
@Test
@@ -246,15 +252,7 @@
.setExemptFromEntitlementCheck(true)
.setShouldShowEntitlementUi(false).build();
final TetheringRequest parceled = ParcelUtils.parcelingRoundTrip(unparceled);
- assertEquals(unparceled.getTetheringType(), parceled.getTetheringType());
- assertEquals(unparceled.getConnectivityScope(), parceled.getConnectivityScope());
- assertEquals(unparceled.getLocalIpv4Address(), parceled.getLocalIpv4Address());
- assertEquals(unparceled.getClientStaticIpv4Address(),
- parceled.getClientStaticIpv4Address());
- assertEquals(unparceled.isExemptFromEntitlementCheck(),
- parceled.isExemptFromEntitlementCheck());
- assertEquals(unparceled.getShouldShowEntitlementUi(),
- parceled.getShouldShowEntitlementUi());
+ assertEquals(unparceled, parceled);
}
@Test
diff --git a/tests/mts/Android.bp b/tests/mts/Android.bp
index c118d0a..9d158fd 100644
--- a/tests/mts/Android.bp
+++ b/tests/mts/Android.bp
@@ -40,6 +40,6 @@
srcs: [
"bpf_existence_test.cpp",
],
- compile_multilib: "first",
+ compile_multilib: "both",
min_sdk_version: "30", // Ensure test runs on R and above.
}
diff --git a/tests/native/connectivity_native_test/Android.bp b/tests/native/connectivity_native_test/Android.bp
index c5088c6..ab2f28c 100644
--- a/tests/native/connectivity_native_test/Android.bp
+++ b/tests/native/connectivity_native_test/Android.bp
@@ -32,7 +32,7 @@
"libmodules-utils-build",
"libutils",
],
- compile_multilib: "first",
+ compile_multilib: "both",
}
filegroup {
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 2f88c41..ef3ebb0 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -92,6 +92,7 @@
"frameworks-net-integration-testutils",
"framework-protos",
"mockito-target-minus-junit4",
+ "modules-utils-build",
"net-tests-utils",
"net-utils-services-common",
"platform-compat-test-rules",
diff --git a/tests/unit/java/com/android/server/CallbackQueueTest.kt b/tests/unit/java/com/android/server/CallbackQueueTest.kt
new file mode 100644
index 0000000..a6dd5c3
--- /dev/null
+++ b/tests/unit/java/com/android/server/CallbackQueueTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.net.ConnectivityManager.CALLBACK_AVAILABLE
+import android.net.ConnectivityManager.CALLBACK_CAP_CHANGED
+import android.net.ConnectivityManager.CALLBACK_IP_CHANGED
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import java.lang.reflect.Modifier
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_NETID_1 = 123
+
+// Maximum 16 bits unsigned value
+private const val TEST_NETID_2 = 0xffff
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class CallbackQueueTest {
+ @Test
+ fun testAddCallback() {
+ val cbs = listOf(
+ TEST_NETID_1 to CALLBACK_AVAILABLE,
+ TEST_NETID_2 to CALLBACK_AVAILABLE,
+ TEST_NETID_1 to CALLBACK_CAP_CHANGED,
+ TEST_NETID_1 to CALLBACK_CAP_CHANGED
+ )
+ val queue = CallbackQueue(intArrayOf()).apply {
+ cbs.forEach { addCallback(it.first, it.second) }
+ }
+
+ assertQueueEquals(cbs, queue)
+ }
+
+ @Test
+ fun testHasCallback() {
+ val queue = CallbackQueue(intArrayOf()).apply {
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
+ addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
+ }
+
+ assertTrue(queue.hasCallback(TEST_NETID_1, CALLBACK_AVAILABLE))
+ assertTrue(queue.hasCallback(TEST_NETID_2, CALLBACK_AVAILABLE))
+ assertTrue(queue.hasCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED))
+
+ assertFalse(queue.hasCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED))
+ assertFalse(queue.hasCallback(1234, CALLBACK_AVAILABLE))
+ assertFalse(queue.hasCallback(TEST_NETID_1, 5678))
+ assertFalse(queue.hasCallback(1234, 5678))
+ }
+
+ @Test
+ fun testRemoveCallbacks() {
+ val queue = CallbackQueue(intArrayOf()).apply {
+ assertFalse(removeCallbacks(TEST_NETID_1, CALLBACK_AVAILABLE))
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
+ addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ assertTrue(removeCallbacks(TEST_NETID_1, CALLBACK_AVAILABLE))
+ }
+ assertQueueEquals(listOf(
+ TEST_NETID_1 to CALLBACK_CAP_CHANGED,
+ TEST_NETID_2 to CALLBACK_AVAILABLE
+ ), queue)
+ }
+
+ @Test
+ fun testRemoveCallbacksForNetId() {
+ val queue = CallbackQueue(intArrayOf()).apply {
+ assertFalse(removeCallbacksForNetId(TEST_NETID_2))
+ addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
+ assertFalse(removeCallbacksForNetId(TEST_NETID_1))
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
+ addCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED)
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_2, CALLBACK_IP_CHANGED)
+ assertTrue(removeCallbacksForNetId(TEST_NETID_2))
+ }
+ assertQueueEquals(listOf(
+ TEST_NETID_1 to CALLBACK_AVAILABLE,
+ TEST_NETID_1 to CALLBACK_CAP_CHANGED,
+ TEST_NETID_1 to CALLBACK_AVAILABLE,
+ ), queue)
+ }
+
+ @Test
+ fun testConstructorFromExistingArray() {
+ val queue1 = CallbackQueue(intArrayOf()).apply {
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
+ }
+ val queue2 = CallbackQueue(queue1.shrinkedBackingArray)
+ assertQueueEquals(listOf(
+ TEST_NETID_1 to CALLBACK_AVAILABLE,
+ TEST_NETID_2 to CALLBACK_AVAILABLE
+ ), queue2)
+ }
+
+ @Test
+ fun testToString() {
+ assertEquals("[]", CallbackQueue(intArrayOf()).toString())
+ assertEquals(
+ "[CALLBACK_AVAILABLE($TEST_NETID_1)]",
+ CallbackQueue(intArrayOf()).apply {
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ }.toString()
+ )
+ assertEquals(
+ "[CALLBACK_AVAILABLE($TEST_NETID_1),CALLBACK_CAP_CHANGED($TEST_NETID_2)]",
+ CallbackQueue(intArrayOf()).apply {
+ addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
+ addCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED)
+ }.toString()
+ )
+ }
+
+ @Test
+ fun testMaxNetId() {
+ // CallbackQueue assumes netIds are at most 16 bits
+ assertTrue(NetIdManager.MAX_NET_ID <= 0xffff)
+ }
+
+ @Test
+ fun testMaxCallbackId() {
+ // CallbackQueue assumes callback IDs are at most 16 bits.
+ val constants = ConnectivityManager::class.java.declaredFields.filter {
+ Modifier.isStatic(it.modifiers) && Modifier.isFinal(it.modifiers) &&
+ it.name.startsWith("CALLBACK_")
+ }
+ constants.forEach {
+ it.isAccessible = true
+ assertTrue(it.get(null) as Int <= 0xffff)
+ }
+ }
+}
+
+private fun assertQueueEquals(expected: List<Pair<Int, Int>>, actual: CallbackQueue) {
+ assertEquals(
+ expected.size,
+ actual.length(),
+ "Size mismatch between expected: $expected and actual: $actual"
+ )
+
+ var nextIndex = 0
+ actual.forEach { netId, cbId ->
+ val (expNetId, expCbId) = expected[nextIndex]
+ val msg = "$actual does not match $expected at index $nextIndex"
+ assertEquals(expNetId, netId, msg)
+ assertEquals(expCbId, cbId, msg)
+ nextIndex++
+ }
+ // Ensure forEach iterations and size are consistent
+ assertEquals(expected.size, nextIndex)
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index be7f2a3..999d17d 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2180,6 +2180,7 @@
case ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK:
case ConnectivityFlags.REQUEST_RESTRICTED_WIFI:
case ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS:
+ case ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS:
case KEY_DESTROY_FROZEN_SOCKETS_VERSION:
return true;
default:
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
index fb3d183..4c71991 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -18,8 +18,10 @@
import static com.android.server.connectivity.mdns.MdnsSocketProvider.SocketCallback;
import static com.android.server.connectivity.mdns.MulticastPacketReader.PacketHandler;
+import static com.android.testutils.Cleanup.testAndCleanup;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -35,6 +37,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.util.Log;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.SharedLog;
@@ -59,6 +62,7 @@
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
@@ -437,4 +441,34 @@
inOrder.verify(mSocket).send(packets.get(i));
}
}
+
+ @Test
+ public void testSendPacketWithMultiplePacketsWithDifferentAddresses() throws IOException {
+ final SocketCallback callback = expectSocketCallback();
+ final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
+ final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */);
+ doReturn(true).when(mSocket).hasJoinedIpv4();
+ doReturn(true).when(mSocket).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+
+ // Notify socket created
+ callback.onSocketCreated(mSocketKey, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
+
+ // Send packets with IPv4 and IPv6 then verify wtf logs and sending has never been called.
+ // Override the default TerribleFailureHandler, as that handler might terminate the process
+ // (if we're on an eng build).
+ final AtomicBoolean hasFailed = new AtomicBoolean(false);
+ final Log.TerribleFailureHandler originalHandler =
+ Log.setWtfHandler((tag, what, system) -> hasFailed.set(true));
+ testAndCleanup(() -> {
+ mSocketClient.sendPacketRequestingMulticastResponse(List.of(ipv4Packet, ipv6Packet),
+ mSocketKey, false /* onlyUseIpv6OnIpv6OnlyNetworks */);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ assertTrue(hasFailed.get());
+ verify(mSocket, never()).send(any());
+ }, () -> Log.setWtfHandler(originalHandler));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 44fa55c..569f4d7 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -562,10 +562,7 @@
//MdnsConfigsFlagsImpl.alwaysAskForUnicastResponseInEachBurst.override(true);
MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
.addSubtype(SUBTYPE).setQueryMode(ACTIVE_QUERY_MODE).build();
- QueryTaskConfig config = new QueryTaskConfig(
- searchOptions.getQueryMode(),
- false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
- socketKey);
+ QueryTaskConfig config = new QueryTaskConfig(searchOptions.getQueryMode());
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -574,14 +571,14 @@
// For the rest of queries in this burst, we will NOT ask for unicast response.
for (int i = 1; i < MdnsConfigs.queriesPerBurst(); i++) {
int oldTransactionId = config.transactionId;
- config = config.getConfigForNextRun();
+ config = config.getConfigForNextRun(ACTIVE_QUERY_MODE);
assertFalse(config.expectUnicastResponse);
assertEquals(config.transactionId, oldTransactionId + 1);
}
// This is the first query of a new burst. We will ask for unicast response.
int oldTransactionId = config.transactionId;
- config = config.getConfigForNextRun();
+ config = config.getConfigForNextRun(ACTIVE_QUERY_MODE);
assertTrue(config.expectUnicastResponse);
assertEquals(config.transactionId, oldTransactionId + 1);
}
@@ -590,10 +587,7 @@
public void testQueryTaskConfig_askForUnicastInFirstQuery() {
MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
.addSubtype(SUBTYPE).setQueryMode(ACTIVE_QUERY_MODE).build();
- QueryTaskConfig config = new QueryTaskConfig(
- searchOptions.getQueryMode(),
- false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
- socketKey);
+ QueryTaskConfig config = new QueryTaskConfig(searchOptions.getQueryMode());
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -602,14 +596,14 @@
// For the rest of queries in this burst, we will NOT ask for unicast response.
for (int i = 1; i < MdnsConfigs.queriesPerBurst(); i++) {
int oldTransactionId = config.transactionId;
- config = config.getConfigForNextRun();
+ config = config.getConfigForNextRun(ACTIVE_QUERY_MODE);
assertFalse(config.expectUnicastResponse);
assertEquals(config.transactionId, oldTransactionId + 1);
}
// This is the first query of a new burst. We will NOT ask for unicast response.
int oldTransactionId = config.transactionId;
- config = config.getConfigForNextRun();
+ config = config.getConfigForNextRun(ACTIVE_QUERY_MODE);
assertFalse(config.expectUnicastResponse);
assertEquals(config.transactionId, oldTransactionId + 1);
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
index 1989ed3..ab70e38 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity.mdns;
+import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertFalse;
@@ -38,9 +39,11 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.InetAddresses;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.text.format.DateUtils;
+import android.util.Log;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.SharedLog;
@@ -594,6 +597,29 @@
}
}
+ @Test
+ public void testSendPacketWithMultiplePacketsWithDifferentAddresses() throws IOException {
+ mdnsClient.startDiscovery();
+ final byte[] buffer = new byte[10];
+ final DatagramPacket ipv4Packet = new DatagramPacket(buffer, 0 /* offset */, buffer.length,
+ InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
+ final DatagramPacket ipv6Packet = new DatagramPacket(buffer, 0 /* offset */, buffer.length,
+ InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */);
+
+ // Send packets with IPv4 and IPv6 then verify wtf logs and sending has never been called.
+ // Override the default TerribleFailureHandler, as that handler might terminate the process
+ // (if we're on an eng build).
+ final AtomicBoolean hasFailed = new AtomicBoolean(false);
+ final Log.TerribleFailureHandler originalHandler =
+ Log.setWtfHandler((tag, what, system) -> hasFailed.set(true));
+ testAndCleanup(() -> {
+ mdnsClient.sendPacketRequestingMulticastResponse(List.of(ipv4Packet, ipv6Packet),
+ false /* onlyUseIpv6OnIpv6OnlyNetworks */);
+ assertTrue(hasFailed.get());
+ verify(mockMulticastSocket, never()).send(any());
+ }, () -> Log.setWtfHandler(originalHandler));
+ }
+
private DatagramPacket getTestDatagramPacket() {
return new DatagramPacket(buf, 0, 5,
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), 5353 /* port */));
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
index 009205e..5c3ad22 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
@@ -16,24 +16,22 @@
package com.android.server.connectivity.mdns.util
+import android.net.InetAddresses
import android.os.Build
import com.android.server.connectivity.mdns.MdnsConstants
import com.android.server.connectivity.mdns.MdnsConstants.FLAG_TRUNCATED
+import com.android.server.connectivity.mdns.MdnsConstants.IPV4_SOCKET_ADDR
+import com.android.server.connectivity.mdns.MdnsConstants.IPV6_SOCKET_ADDR
import com.android.server.connectivity.mdns.MdnsPacket
import com.android.server.connectivity.mdns.MdnsPacketReader
import com.android.server.connectivity.mdns.MdnsPointerRecord
import com.android.server.connectivity.mdns.MdnsRecord
import com.android.server.connectivity.mdns.util.MdnsUtils.createQueryDatagramPackets
-import com.android.server.connectivity.mdns.util.MdnsUtils.equalsDnsLabelIgnoreDnsCase
-import com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase
-import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLabelsLowerCase
-import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLowerCase
import com.android.server.connectivity.mdns.util.MdnsUtils.truncateServiceName
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import java.net.DatagramPacket
import kotlin.test.assertContentEquals
-import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -43,59 +41,6 @@
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsUtilsTest {
- @Test
- fun testToDnsLowerCase() {
- assertEquals("test", toDnsLowerCase("TEST"))
- assertEquals("test", toDnsLowerCase("TeSt"))
- assertEquals("test", toDnsLowerCase("test"))
- assertEquals("tÉst", toDnsLowerCase("TÉST"))
- assertEquals("ţést", toDnsLowerCase("ţést"))
- // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
- // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
- assertEquals(
- "test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
- toDnsLowerCase("Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ")
- )
- // Also test some characters where the first surrogate is not \ud800
- assertEquals(
- "test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
- "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
- toDnsLowerCase(
- "Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
- "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"
- )
- )
- }
-
- @Test
- fun testToDnsLabelsLowerCase() {
- assertArrayEquals(
- arrayOf("test", "tÉst", "ţést"),
- toDnsLabelsLowerCase(arrayOf("TeSt", "TÉST", "ţést"))
- )
- }
-
- @Test
- fun testEqualsIgnoreDnsCase() {
- assertTrue(equalsIgnoreDnsCase("TEST", "Test"))
- assertTrue(equalsIgnoreDnsCase("TEST", "test"))
- assertTrue(equalsIgnoreDnsCase("test", "TeSt"))
- assertTrue(equalsIgnoreDnsCase("Tést", "tést"))
- assertFalse(equalsIgnoreDnsCase("ŢÉST", "ţést"))
- // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
- // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
- assertTrue(equalsIgnoreDnsCase(
- "test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
- "Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- "
- ))
- // Also test some characters where the first surrogate is not \ud800
- assertTrue(equalsIgnoreDnsCase(
- "test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
- "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
- "Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
- "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"
- ))
- }
@Test
fun testTruncateServiceName() {
@@ -104,14 +49,6 @@
}
@Test
- fun testEqualsLabelIgnoreDnsCase() {
- assertTrue(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "test")))
- assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test")))
- assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("Test"), arrayOf("test", "test")))
- assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "tést")))
- }
-
- @Test
fun testTypeEqualsOrIsSubtype() {
assertTrue(MdnsUtils.typeEqualsOrIsSubtype(
arrayOf("_type", "_tcp", "local"),
@@ -193,4 +130,31 @@
}
return MdnsPacket(flags, questions, answers, emptyList(), emptyList())
}
+
+ @Test
+ fun testCheckAllPacketsWithSameAddress() {
+ val buffer = ByteArray(10)
+ val v4Packet = DatagramPacket(buffer, buffer.size, IPV4_SOCKET_ADDR)
+ val otherV4Packet = DatagramPacket(
+ buffer,
+ buffer.size,
+ InetAddresses.parseNumericAddress("192.0.2.1"),
+ 1234
+ )
+ val v6Packet = DatagramPacket(ByteArray(10), 10, IPV6_SOCKET_ADDR)
+ val otherV6Packet = DatagramPacket(
+ buffer,
+ buffer.size,
+ InetAddresses.parseNumericAddress("2001:db8::"),
+ 1234
+ )
+ assertTrue(MdnsUtils.checkAllPacketsWithSameAddress(listOf()))
+ assertTrue(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v4Packet)))
+ assertTrue(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v4Packet, v4Packet)))
+ assertFalse(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v4Packet, otherV4Packet)))
+ assertTrue(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v6Packet)))
+ assertTrue(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v6Packet, v6Packet)))
+ assertFalse(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v6Packet, otherV6Packet)))
+ assertFalse(MdnsUtils.checkAllPacketsWithSameAddress(listOf(v4Packet, v6Packet)))
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
index 3ad8de8..985d403 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
@@ -419,4 +419,30 @@
deps.setChangeIdEnabled(true, NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION)
doTestBlockedReasonsNoInternetPermission(blockedByNoInternetPermission = true)
}
+
+ private fun doTestEnforceMeteredApnPolicy(restricted: Boolean) {
+ doReturn(restricted).`when`(bpfNetMaps).isUidRestrictedOnMeteredNetworks(Process.myUid())
+
+ val cellAgent = Agent(nc = cellNc())
+ cellAgent.connect()
+ val cb = TestableNetworkCallback()
+ cm.requestNetwork(cellRequest(), cb)
+
+ if (restricted) {
+ waitForIdle()
+ cb.assertNoCallback()
+ } else {
+ cb.expectAvailableCallbacks(cellAgent.network, validated = false)
+ }
+ }
+
+ @Test
+ fun testEnforceMeteredApnPolicy_restricted() {
+ doTestEnforceMeteredApnPolicy(restricted = true)
+ }
+
+ @Test
+ fun testEnforceMeteredApnPolicy_notRestricted() {
+ doTestEnforceMeteredApnPolicy(restricted = false)
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSDeclaredMethodsForCallbacksTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSDeclaredMethodsForCallbacksTest.kt
index cf990b1..a7083dc 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSDeclaredMethodsForCallbacksTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSDeclaredMethodsForCallbacksTest.kt
@@ -17,8 +17,11 @@
package com.android.server.connectivityservice
import android.net.ConnectivityManager
+import android.net.ConnectivityManager.CALLBACK_AVAILABLE
+import android.net.ConnectivityManager.CALLBACK_BLK_CHANGED
import android.net.ConnectivityManager.CALLBACK_CAP_CHANGED
import android.net.ConnectivityManager.CALLBACK_IP_CHANGED
+import android.net.ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED
import android.net.ConnectivityManager.CALLBACK_LOST
import android.net.ConnectivityManager.NetworkCallback.DECLARED_METHODS_ALL
import android.net.LinkAddress
@@ -35,7 +38,9 @@
import com.android.testutils.RecorderCallback.CallbackEntry
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.tryTest
+import java.lang.reflect.Modifier
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.test.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -129,6 +134,32 @@
listenCb.expect<CallbackEntry.CapabilitiesChanged>()
listenCb.assertNoCallback(timeoutMs = 0L)
}
+
+ @Test
+ fun testDeclaredMethodsFlagsToString() {
+ assertEquals("NONE", ConnectivityService.declaredMethodsFlagsToString(0))
+ assertEquals("ALL", ConnectivityService.declaredMethodsFlagsToString(0.inv()))
+ assertEquals("AVAIL|NC|LP|BLK|LOCALINF", ConnectivityService.declaredMethodsFlagsToString(
+ (1 shl CALLBACK_AVAILABLE) or
+ (1 shl CALLBACK_CAP_CHANGED) or
+ (1 shl CALLBACK_IP_CHANGED) or
+ (1 shl CALLBACK_BLK_CHANGED) or
+ (1 shl CALLBACK_LOCAL_NETWORK_INFO_CHANGED)
+ ))
+
+ // EXPIRE_LEGACY_REQUEST (=8) is only used in ConnectivityManager and not included.
+ // CALLBACK_TRANSITIVE_CALLS_ONLY (=0) is not a callback so not included either.
+ assertEquals(
+ "PRECHK|AVAIL|LOSING|LOST|UNAVAIL|NC|LP|SUSP|RESUME|BLK|LOCALINF|0x7fffe101",
+ ConnectivityService.declaredMethodsFlagsToString(0x7fff_ffff)
+ )
+ // The toString method and the assertion above need to be updated if constants are added
+ val constants = ConnectivityManager::class.java.declaredFields.filter {
+ Modifier.isStatic(it.modifiers) && Modifier.isFinal(it.modifiers) &&
+ it.name.startsWith("CALLBACK_")
+ }
+ assertEquals(12, constants.size)
+ }
}
private fun AtomicInteger.withFlags(vararg flags: Int, action: () -> Unit) {
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 ed72fd2..de56ae5 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -165,6 +165,7 @@
it[ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN] = true
it[ConnectivityFlags.DELAY_DESTROY_SOCKETS] = true
it[ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS] = true
+ it[ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS] = true
}
fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
diff --git a/thread/framework/java/android/net/thread/ThreadConfiguration.java b/thread/framework/java/android/net/thread/ThreadConfiguration.java
index e09b3a6..be2632c 100644
--- a/thread/framework/java/android/net/thread/ThreadConfiguration.java
+++ b/thread/framework/java/android/net/thread/ThreadConfiguration.java
@@ -15,7 +15,9 @@
*/
package android.net.thread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,19 +39,19 @@
* @see ThreadNetworkController#unregisterConfigurationCallback
* @hide
*/
-// @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
-// @SystemApi
+@FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
+@SystemApi
public final class ThreadConfiguration implements Parcelable {
private final boolean mNat64Enabled;
- private final boolean mDhcp6PdEnabled;
+ private final boolean mDhcpv6PdEnabled;
private ThreadConfiguration(Builder builder) {
- this(builder.mNat64Enabled, builder.mDhcp6PdEnabled);
+ this(builder.mNat64Enabled, builder.mDhcpv6PdEnabled);
}
- private ThreadConfiguration(boolean nat64Enabled, boolean dhcp6PdEnabled) {
+ private ThreadConfiguration(boolean nat64Enabled, boolean dhcpv6PdEnabled) {
this.mNat64Enabled = nat64Enabled;
- this.mDhcp6PdEnabled = dhcp6PdEnabled;
+ this.mDhcpv6PdEnabled = dhcpv6PdEnabled;
}
/** Returns {@code true} if NAT64 is enabled. */
@@ -58,8 +60,8 @@
}
/** Returns {@code true} if DHCPv6 Prefix Delegation is enabled. */
- public boolean isDhcp6PdEnabled() {
- return mDhcp6PdEnabled;
+ public boolean isDhcpv6PdEnabled() {
+ return mDhcpv6PdEnabled;
}
@Override
@@ -71,13 +73,13 @@
} else {
ThreadConfiguration otherConfig = (ThreadConfiguration) other;
return mNat64Enabled == otherConfig.mNat64Enabled
- && mDhcp6PdEnabled == otherConfig.mDhcp6PdEnabled;
+ && mDhcpv6PdEnabled == otherConfig.mDhcpv6PdEnabled;
}
}
@Override
public int hashCode() {
- return Objects.hash(mNat64Enabled, mDhcp6PdEnabled);
+ return Objects.hash(mNat64Enabled, mDhcpv6PdEnabled);
}
@Override
@@ -85,7 +87,7 @@
StringBuilder sb = new StringBuilder();
sb.append('{');
sb.append("Nat64Enabled=").append(mNat64Enabled);
- sb.append(", Dhcp6PdEnabled=").append(mDhcp6PdEnabled);
+ sb.append(", Dhcpv6PdEnabled=").append(mDhcpv6PdEnabled);
sb.append('}');
return sb.toString();
}
@@ -98,7 +100,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mNat64Enabled);
- dest.writeBoolean(mDhcp6PdEnabled);
+ dest.writeBoolean(mDhcpv6PdEnabled);
}
public static final @NonNull Creator<ThreadConfiguration> CREATOR =
@@ -107,7 +109,7 @@
public ThreadConfiguration createFromParcel(Parcel in) {
ThreadConfiguration.Builder builder = new ThreadConfiguration.Builder();
builder.setNat64Enabled(in.readBoolean());
- builder.setDhcp6PdEnabled(in.readBoolean());
+ builder.setDhcpv6PdEnabled(in.readBoolean());
return builder.build();
}
@@ -117,10 +119,14 @@
}
};
- /** The builder for creating {@link ThreadConfiguration} objects. */
+ /**
+ * The builder for creating {@link ThreadConfiguration} objects.
+ *
+ * @hide
+ */
public static final class Builder {
private boolean mNat64Enabled = false;
- private boolean mDhcp6PdEnabled = false;
+ private boolean mDhcpv6PdEnabled = false;
/** Creates a new {@link Builder} object with all features disabled. */
public Builder() {}
@@ -134,7 +140,7 @@
Objects.requireNonNull(config);
mNat64Enabled = config.mNat64Enabled;
- mDhcp6PdEnabled = config.mDhcp6PdEnabled;
+ mDhcpv6PdEnabled = config.mDhcpv6PdEnabled;
}
/**
@@ -156,8 +162,8 @@
* IPv6.
*/
@NonNull
- public Builder setDhcp6PdEnabled(boolean enabled) {
- this.mDhcp6PdEnabled = enabled;
+ public Builder setDhcpv6PdEnabled(boolean enabled) {
+ this.mDhcpv6PdEnabled = enabled;
return this;
}
diff --git a/thread/framework/java/android/net/thread/ThreadNetworkController.java b/thread/framework/java/android/net/thread/ThreadNetworkController.java
index 30b3d6a..b4e581c 100644
--- a/thread/framework/java/android/net/thread/ThreadNetworkController.java
+++ b/thread/framework/java/android/net/thread/ThreadNetworkController.java
@@ -619,16 +619,15 @@
/**
* Registers a callback to be called when the configuration is changed.
*
- * <p>Upon return of this method, {@code callback} will be invoked immediately with the new
+ * <p>Upon return of this method, {@code callback} will be invoked immediately with the current
* {@link ThreadConfiguration}.
*
* @param executor the executor to execute the {@code callback}
* @param callback the callback to receive Thread configuration changes
* @throws IllegalArgumentException if {@code callback} has already been registered
- * @hide
*/
- // @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
- // @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
+ @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
+ @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
public void registerConfigurationCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<ThreadConfiguration> callback) {
@@ -656,10 +655,9 @@
* @param callback the callback which has been registered with {@link
* #registerConfigurationCallback}
* @throws IllegalArgumentException if {@code callback} hasn't been registered
- * @hide
*/
- // @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
- // @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
+ @FlaggedApi(ThreadNetworkFlags.FLAG_CONFIGURATION_ENABLED)
+ @RequiresPermission(permission.THREAD_NETWORK_PRIVILEGED)
public void unregisterConfigurationCallback(@NonNull Consumer<ThreadConfiguration> callback) {
requireNonNull(callback, "callback cannot be null");
synchronized (mConfigurationCallbackMapLock) {
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 2f60d9a..e6f272b 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -111,7 +111,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
@@ -139,6 +138,7 @@
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -210,7 +210,6 @@
private final ThreadPersistentSettings mPersistentSettings;
private final UserManager mUserManager;
private boolean mUserRestricted;
- private boolean mAirplaneModeOn;
private boolean mForceStopOtDaemonEnabled;
private BorderRouterConfigurationParcel mBorderRouterConfig;
@@ -344,8 +343,8 @@
final String modelName = resources.getString(R.string.config_thread_model_name);
final String vendorName = resources.getString(R.string.config_thread_vendor_name);
final String vendorOui = resources.getString(R.string.config_thread_vendor_oui);
- final boolean managedByGoogle =
- resources.getBoolean(R.bool.config_thread_managed_by_google_home);
+ final String[] vendorSpecificTxts =
+ resources.getStringArray(R.array.config_thread_mdns_vendor_specific_txts);
if (!modelName.isEmpty()) {
if (modelName.getBytes(UTF_8).length > MAX_MODEL_NAME_UTF8_BYTES) {
@@ -375,19 +374,44 @@
meshcopTxts.modelName = modelName;
meshcopTxts.vendorName = vendorName;
meshcopTxts.vendorOui = HexEncoding.decode(vendorOui.replace("-", "").replace(":", ""));
- meshcopTxts.nonStandardTxtEntries = List.of(makeManagedByGoogleTxtAttr(managedByGoogle));
+ meshcopTxts.nonStandardTxtEntries = makeVendorSpecificTxtAttrs(vendorSpecificTxts);
return meshcopTxts;
}
/**
- * Creates a DNS-SD TXT entry for indicating whether Thread on this device is managed by Google.
+ * Parses vendor-specific TXT entries from "=" separated strings into list of {@link
+ * DnsTxtAttribute}.
*
- * @return TXT entry "vgh=1" if {@code managedByGoogle} is {@code true}; otherwise, "vgh=0"
+ * @throws IllegalArgumentsException if invalid TXT entries are found in {@code vendorTxts}
*/
- private static DnsTxtAttribute makeManagedByGoogleTxtAttr(boolean managedByGoogle) {
- final byte[] value = (managedByGoogle ? "1" : "0").getBytes(UTF_8);
- return new DnsTxtAttribute("vgh", value);
+ @VisibleForTesting
+ static List<DnsTxtAttribute> makeVendorSpecificTxtAttrs(String[] vendorTxts) {
+ List<DnsTxtAttribute> txts = new ArrayList<>();
+ for (String txt : vendorTxts) {
+ String[] kv = txt.split("=", 2 /* limit */); // Split with only the first '='
+ if (kv.length < 1) {
+ throw new IllegalArgumentException(
+ "Invalid vendor-specific TXT is found in resources: " + txt);
+ }
+
+ if (kv[0].length() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid vendor-specific TXT key \""
+ + kv[0]
+ + "\": it must contain at least 2 characters");
+ }
+
+ if (!kv[0].startsWith("v")) {
+ throw new IllegalArgumentException(
+ "Invalid vendor-specific TXT key \""
+ + kv[0]
+ + "\": it doesn't start with \"v\"");
+ }
+
+ txts.add(new DnsTxtAttribute(kv[0], (kv.length >= 2 ? kv[1] : "").getBytes(UTF_8)));
+ }
+ return txts;
}
private void onOtDaemonDied() {
@@ -420,8 +444,6 @@
requestThreadNetwork();
mUserRestricted = isThreadUserRestricted();
registerUserRestrictionsReceiver();
- mAirplaneModeOn = isAirplaneModeOn();
- registerAirplaneModeReceiver();
maybeInitializeOtDaemon();
});
}
@@ -503,15 +525,6 @@
// the otDaemon set enabled state operation succeeded or not, so that it can recover
// to the desired value after reboot.
mPersistentSettings.put(ThreadPersistentSettings.THREAD_ENABLED.key, isEnabled);
-
- // Remember whether the user wanted to keep Thread enabled in airplane mode. If once
- // the user disabled Thread again in airplane mode, the persistent settings state is
- // reset (so that Thread will be auto-disabled again when airplane mode is turned on).
- // This behavior is consistent with Wi-Fi and bluetooth.
- if (mAirplaneModeOn) {
- mPersistentSettings.put(
- ThreadPersistentSettings.THREAD_ENABLED_IN_AIRPLANE_MODE.key, isEnabled);
- }
}
try {
@@ -598,10 +611,12 @@
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mHandler.post(() -> onUserRestrictionsChanged(isThreadUserRestricted()));
+ onUserRestrictionsChanged(isThreadUserRestricted());
}
},
- new IntentFilter(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
+ new IntentFilter(UserManager.ACTION_USER_RESTRICTIONS_CHANGED),
+ null /* broadcastPermission */,
+ mHandler);
}
private void onUserRestrictionsChanged(boolean newUserRestrictedState) {
@@ -648,72 +663,13 @@
return mUserManager.hasUserRestriction(DISALLOW_THREAD_NETWORK);
}
- private void registerAirplaneModeReceiver() {
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.post(() -> onAirplaneModeChanged(isAirplaneModeOn()));
- }
- },
- new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
- }
-
- private void onAirplaneModeChanged(boolean newAirplaneModeOn) {
- checkOnHandlerThread();
- if (mAirplaneModeOn == newAirplaneModeOn) {
- return;
- }
- Log.i(TAG, "Airplane mode changed: " + mAirplaneModeOn + " -> " + newAirplaneModeOn);
- mAirplaneModeOn = newAirplaneModeOn;
-
- final boolean shouldEnableThread = shouldEnableThread();
- final IOperationReceiver receiver =
- new IOperationReceiver.Stub() {
- @Override
- public void onSuccess() {
- Log.d(
- TAG,
- (shouldEnableThread ? "Enabled" : "Disabled")
- + " Thread due to airplane mode change");
- }
-
- @Override
- public void onError(int errorCode, String errorMessage) {
- Log.e(
- TAG,
- "Failed to "
- + (shouldEnableThread ? "enable" : "disable")
- + " Thread for airplane mode change");
- }
- };
- // Do not save the user restriction state to persistent settings so that the user
- // configuration won't be overwritten
- setEnabledInternal(
- shouldEnableThread, false /* persist */, new OperationReceiverWrapper(receiver));
- }
-
- /** Returns {@code true} if Airplane mode has been turned on. */
- private boolean isAirplaneModeOn() {
- return Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0)
- == 1;
- }
-
/**
* Returns {@code true} if Thread should be enabled based on current settings, runtime user
- * restriction and airplane mode state.
+ * restriction state.
*/
private boolean shouldEnableThread() {
- final boolean enabledInAirplaneMode =
- mPersistentSettings.get(ThreadPersistentSettings.THREAD_ENABLED_IN_AIRPLANE_MODE);
-
return !mForceStopOtDaemonEnabled
&& !mUserRestricted
- // FIXME(b/340744397): Note that here we need to call `isAirplaneModeOn()` to get
- // the latest state of airplane mode but can't use `mIsAirplaneMode`. This is for
- // avoiding the race conditions described in b/340744397
- && (!isAirplaneModeOn() || enabledInAirplaneMode)
&& mPersistentSettings.get(ThreadPersistentSettings.THREAD_ENABLED);
}
diff --git a/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java b/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
index 747cc96..7c4c72d 100644
--- a/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
+++ b/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
@@ -197,7 +197,7 @@
return false;
}
putObject(CONFIG_NAT64_ENABLED.key, configuration.isNat64Enabled());
- putObject(CONFIG_DHCP6_PD_ENABLED.key, configuration.isDhcp6PdEnabled());
+ putObject(CONFIG_DHCP6_PD_ENABLED.key, configuration.isDhcpv6PdEnabled());
writeToStoreFile();
return true;
}
@@ -206,7 +206,7 @@
public ThreadConfiguration getConfiguration() {
return new ThreadConfiguration.Builder()
.setNat64Enabled(get(CONFIG_NAT64_ENABLED))
- .setDhcp6PdEnabled(get(CONFIG_DHCP6_PD_ENABLED))
+ .setDhcpv6PdEnabled(get(CONFIG_DHCP6_PD_ENABLED))
.build();
}
diff --git a/thread/tests/cts/Android.bp b/thread/tests/cts/Android.bp
index c1cf0a0..6db7c9c 100644
--- a/thread/tests/cts/Android.bp
+++ b/thread/tests/cts/Android.bp
@@ -21,9 +21,11 @@
android_test {
name: "CtsThreadNetworkTestCases",
- defaults: ["cts_defaults"],
+ defaults: [
+ "cts_defaults",
+ "framework-connectivity-test-defaults",
+ ],
min_sdk_version: "33",
- sdk_version: "test_current",
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
srcs: [
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadConfigurationTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadConfigurationTest.java
new file mode 100644
index 0000000..386412e
--- /dev/null
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadConfigurationTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.thread.cts;
+
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.thread.ThreadConfiguration;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/** Tests for {@link ThreadConfiguration}. */
+@SmallTest
+@RequiresThreadFeature
+@RunWith(Parameterized.class)
+public final class ThreadConfigurationTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
+ public final boolean mIsNat64Enabled;
+ public final boolean mIsDhcpv6PdEnabled;
+
+ @Parameterized.Parameters
+ public static Collection configArguments() {
+ return Arrays.asList(
+ new Object[][] {
+ {false, false}, // All disabled
+ {true, false}, // NAT64 enabled
+ {false, true}, // DHCP6-PD enabled
+ {true, true}, // All enabled
+ });
+ }
+
+ public ThreadConfigurationTest(boolean isNat64Enabled, boolean isDhcpv6PdEnabled) {
+ mIsNat64Enabled = isNat64Enabled;
+ mIsDhcpv6PdEnabled = isDhcpv6PdEnabled;
+ }
+
+ @Test
+ public void parcelable_parcelingIsLossLess() {
+ ThreadConfiguration config =
+ new ThreadConfiguration.Builder()
+ .setNat64Enabled(mIsNat64Enabled)
+ .setDhcpv6PdEnabled(mIsDhcpv6PdEnabled)
+ .build();
+ assertParcelingIsLossless(config);
+ }
+
+ @Test
+ public void builder_correctValuesAreSet() {
+ ThreadConfiguration config =
+ new ThreadConfiguration.Builder()
+ .setNat64Enabled(mIsNat64Enabled)
+ .setDhcpv6PdEnabled(mIsDhcpv6PdEnabled)
+ .build();
+
+ assertThat(config.isNat64Enabled()).isEqualTo(mIsNat64Enabled);
+ assertThat(config.isDhcpv6PdEnabled()).isEqualTo(mIsDhcpv6PdEnabled);
+ }
+
+ @Test
+ public void builderConstructor_configsAreEqual() {
+ ThreadConfiguration config1 =
+ new ThreadConfiguration.Builder()
+ .setNat64Enabled(mIsNat64Enabled)
+ .setDhcpv6PdEnabled(mIsDhcpv6PdEnabled)
+ .build();
+ ThreadConfiguration config2 = new ThreadConfiguration.Builder(config1).build();
+ assertThat(config1).isEqualTo(config2);
+ }
+}
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 41f34ff..1a101b6 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -58,6 +58,7 @@
import android.net.thread.ActiveOperationalDataset;
import android.net.thread.OperationalDatasetTimestamp;
import android.net.thread.PendingOperationalDataset;
+import android.net.thread.ThreadConfiguration;
import android.net.thread.ThreadNetworkController;
import android.net.thread.ThreadNetworkController.OperationalDatasetCallback;
import android.net.thread.ThreadNetworkController.StateCallback;
@@ -95,9 +96,11 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/** CTS tests for {@link ThreadNetworkController}. */
@@ -110,11 +113,14 @@
private static final int NETWORK_CALLBACK_TIMEOUT_MILLIS = 10 * 1000;
private static final int CALLBACK_TIMEOUT_MILLIS = 1_000;
private static final int ENABLED_TIMEOUT_MILLIS = 2_000;
+ private static final int SET_CONFIGURATION_TIMEOUT_MILLIS = 1_000;
private static final int SERVICE_DISCOVERY_TIMEOUT_MILLIS = 30_000;
private static final int SERVICE_LOST_TIMEOUT_MILLIS = 20_000;
private static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp";
private static final String THREAD_NETWORK_PRIVILEGED =
"android.permission.THREAD_NETWORK_PRIVILEGED";
+ private static final ThreadConfiguration DEFAULT_CONFIG =
+ new ThreadConfiguration.Builder().build();
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
@@ -127,6 +133,9 @@
private HandlerThread mHandlerThread;
private TapTestNetworkTracker mTestNetworkTracker;
+ private final List<Consumer<ThreadConfiguration>> mConfigurationCallbacksToCleanUp =
+ new ArrayList<>();
+
@Before
public void setUp() throws Exception {
mController =
@@ -141,6 +150,7 @@
mHandlerThread.start();
setEnabledAndWait(mController, true);
+ setConfigurationAndWait(mController, DEFAULT_CONFIG);
}
@After
@@ -148,6 +158,18 @@
dropAllPermissions();
leaveAndWait(mController);
tearDownTestNetwork();
+ setConfigurationAndWait(mController, DEFAULT_CONFIG);
+ for (Consumer<ThreadConfiguration> configurationCallback :
+ mConfigurationCallbacksToCleanUp) {
+ try {
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> mController.unregisterConfigurationCallback(configurationCallback));
+ } catch (IllegalArgumentException e) {
+ // Ignore the exception when the callback is not registered.
+ }
+ }
+ mConfigurationCallbacksToCleanUp.clear();
}
@Test
@@ -832,6 +854,152 @@
NET_CAPABILITY_TRUSTED);
}
+ @Test
+ public void setConfiguration_null_throwsNullPointerException() throws Exception {
+ CompletableFuture<Void> setConfigFuture = new CompletableFuture<>();
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ mController.setConfiguration(
+ null, mExecutor, newOutcomeReceiver(setConfigFuture)));
+ }
+
+ @Test
+ public void setConfiguration_noPermissions_throwsSecurityException() throws Exception {
+ ThreadConfiguration configuration =
+ new ThreadConfiguration.Builder().setNat64Enabled(true).build();
+ CompletableFuture<Void> setConfigFuture = new CompletableFuture<>();
+ assertThrows(
+ SecurityException.class,
+ () -> {
+ mController.setConfiguration(
+ configuration, mExecutor, newOutcomeReceiver(setConfigFuture));
+ });
+ }
+
+ @Test
+ public void registerConfigurationCallback_permissionsGranted_returnsCurrentStatus()
+ throws Exception {
+ CompletableFuture<ThreadConfiguration> getConfigFuture = new CompletableFuture<>();
+ Consumer<ThreadConfiguration> callback = getConfigFuture::complete;
+
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> registerConfigurationCallback(mController, mExecutor, callback));
+ assertThat(getConfigFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS))
+ .isEqualTo(DEFAULT_CONFIG);
+ }
+
+ @Test
+ public void registerConfigurationCallback_noPermissions_throwsSecurityException()
+ throws Exception {
+ dropAllPermissions();
+
+ assertThrows(
+ SecurityException.class,
+ () -> registerConfigurationCallback(mController, mExecutor, config -> {}));
+ }
+
+ @Test
+ public void registerConfigurationCallback_returnsUpdatedConfigurations() throws Exception {
+ CompletableFuture<Void> setFuture1 = new CompletableFuture<>();
+ CompletableFuture<Void> setFuture2 = new CompletableFuture<>();
+ ConfigurationListener listener = new ConfigurationListener(mController);
+ ThreadConfiguration config1 =
+ new ThreadConfiguration.Builder()
+ .setNat64Enabled(true)
+ .setDhcpv6PdEnabled(true)
+ .build();
+ ThreadConfiguration config2 =
+ new ThreadConfiguration.Builder()
+ .setNat64Enabled(false)
+ .setDhcpv6PdEnabled(true)
+ .build();
+
+ try {
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () ->
+ mController.setConfiguration(
+ config1, mExecutor, newOutcomeReceiver(setFuture1)));
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () ->
+ mController.setConfiguration(
+ config2, mExecutor, newOutcomeReceiver(setFuture2)));
+ setFuture1.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS);
+ setFuture2.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS);
+
+ listener.expectConfiguration(DEFAULT_CONFIG);
+ listener.expectConfiguration(config1);
+ listener.expectConfiguration(config2);
+ listener.expectNoMoreConfiguration();
+ } finally {
+ listener.unregisterConfigurationCallback();
+ }
+ }
+
+ @Test
+ public void registerConfigurationCallback_alreadyRegistered_throwsIllegalArgumentException()
+ throws Exception {
+ grantPermissions(THREAD_NETWORK_PRIVILEGED);
+
+ Consumer<ThreadConfiguration> callback = config -> {};
+ registerConfigurationCallback(mController, mExecutor, callback);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> registerConfigurationCallback(mController, mExecutor, callback));
+ }
+
+ @Test
+ public void unregisterConfigurationCallback_noPermissions_throwsSecurityException()
+ throws Exception {
+ Consumer<ThreadConfiguration> callback = config -> {};
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> registerConfigurationCallback(mController, mExecutor, callback));
+
+ assertThrows(
+ SecurityException.class,
+ () -> mController.unregisterConfigurationCallback(callback));
+ }
+
+ @Test
+ public void unregisterConfigurationCallback_callbackRegistered_success() throws Exception {
+ Consumer<ThreadConfiguration> callback = config -> {};
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> {
+ registerConfigurationCallback(mController, mExecutor, callback);
+ mController.unregisterConfigurationCallback(callback);
+ });
+ }
+
+ @Test
+ public void
+ unregisterConfigurationCallback_callbackNotRegistered_throwsIllegalArgumentException()
+ throws Exception {
+ Consumer<ThreadConfiguration> callback = config -> {};
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mController.unregisterConfigurationCallback(callback));
+ }
+
+ @Test
+ public void unregisterConfigurationCallback_alreadyUnregistered_throwsIllegalArgumentException()
+ throws Exception {
+ grantPermissions(THREAD_NETWORK_PRIVILEGED);
+
+ Consumer<ThreadConfiguration> callback = config -> {};
+ registerConfigurationCallback(mController, mExecutor, callback);
+ mController.unregisterConfigurationCallback(callback);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mController.unregisterConfigurationCallback(callback));
+ }
+
private void grantPermissions(String... permissions) {
for (String permission : permissions) {
mGrantedPermissions.add(permission);
@@ -859,7 +1027,6 @@
assertThat(txtMap.get("rv")).isNotNull();
assertThat(txtMap.get("tv")).isNotNull();
assertThat(txtMap.get("sb")).isNotNull();
- assertThat(new String(txtMap.get("vgh"))).isIn(List.of("0", "1"));
}
@Test
@@ -886,7 +1053,6 @@
assertThat(txtMap.get("tv")).isNotNull();
assertThat(txtMap.get("sb")).isNotNull();
assertThat(txtMap.get("id").length).isEqualTo(16);
- assertThat(new String(txtMap.get("vgh"))).isIn(List.of("0", "1"));
}
@Test
@@ -1040,6 +1206,35 @@
}
}
+ private class ConfigurationListener {
+ private ArrayTrackRecord<ThreadConfiguration> mConfigurations = new ArrayTrackRecord<>();
+ private final ArrayTrackRecord<ThreadConfiguration>.ReadHead mReadHead =
+ mConfigurations.newReadHead();
+ ThreadNetworkController mController;
+ Consumer<ThreadConfiguration> mCallback = (config) -> mConfigurations.add(config);
+
+ ConfigurationListener(ThreadNetworkController controller) {
+ this.mController = controller;
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> controller.registerConfigurationCallback(mExecutor, mCallback));
+ }
+
+ public void expectConfiguration(ThreadConfiguration config) {
+ assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, c -> c.equals(config))).isNotNull();
+ }
+
+ public void expectNoMoreConfiguration() {
+ assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, c -> true)).isNull();
+ }
+
+ public void unregisterConfigurationCallback() {
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () -> mController.unregisterConfigurationCallback(mCallback));
+ }
+ }
+
private int booleanToEnabledState(boolean enabled) {
return enabled ? STATE_ENABLED : STATE_DISABLED;
}
@@ -1054,6 +1249,18 @@
waitForEnabledState(controller, booleanToEnabledState(enabled));
}
+ private void setConfigurationAndWait(
+ ThreadNetworkController controller, ThreadConfiguration configuration)
+ throws Exception {
+ CompletableFuture<Void> setFuture = new CompletableFuture<>();
+ runAsShell(
+ THREAD_NETWORK_PRIVILEGED,
+ () ->
+ controller.setConfiguration(
+ configuration, mExecutor, newOutcomeReceiver(setFuture)));
+ setFuture.get(SET_CONFIGURATION_TIMEOUT_MILLIS, MILLISECONDS);
+ }
+
private CompletableFuture joinRandomizedDataset(
ThreadNetworkController controller, String networkName) throws Exception {
ActiveOperationalDataset activeDataset = newRandomizedDataset(networkName, controller);
@@ -1120,6 +1327,14 @@
};
}
+ private void registerConfigurationCallback(
+ ThreadNetworkController controller,
+ Executor executor,
+ Consumer<ThreadConfiguration> callback) {
+ controller.registerConfigurationCallback(executor, callback);
+ mConfigurationCallbacksToCleanUp.add(callback);
+ }
+
private static void assertDoesNotThrow(ThrowingRunnable runnable) {
try {
runnable.run();
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index 8c63d37..b6d9aa3 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -17,12 +17,13 @@
package android.net.thread;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.DEFAULT_DATASET;
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;
import static android.net.thread.utils.IntegrationTestUtils.isToIpv6Destination;
+import static android.net.thread.utils.IntegrationTestUtils.joinNetworkAndWaitForOmr;
import static android.net.thread.utils.IntegrationTestUtils.newPacketReader;
import static android.net.thread.utils.IntegrationTestUtils.pollForPacket;
import static android.net.thread.utils.IntegrationTestUtils.sendUdpMessage;
@@ -33,7 +34,6 @@
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
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;
@@ -95,17 +95,6 @@
private static final Inet6Address GROUP_ADDR_SCOPE_3 =
(Inet6Address) InetAddresses.parseNumericAddress("ff03::1234");
- // A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
- private static final byte[] DEFAULT_DATASET_TLVS =
- base16().decode(
- "0E080000000000010000000300001335060004001FFFE002"
- + "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
- + "53760F519A63BAFDDFFC80D2AF030F4F70656E5468726561"
- + "642D643961300102D9A00410A245479C836D551B9CA557F7"
- + "B9D351B40C0402A0FFF8");
- private static final ActiveOperationalDataset DEFAULT_DATASET =
- ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
-
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@@ -171,7 +160,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
mInfraDevice.sendEchoRequest(ftd.getOmrAddress());
@@ -193,7 +182,7 @@
startInfraDeviceAndWaitForOnLinkAddr();
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
mInfraDevice.sendEchoRequest(ftd.getOmrAddress());
@@ -213,7 +202,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
Inet6Address ftdOmr = ftd.getOmrAddress();
// Create a new infra network and let Thread prefer it
TestNetworkTracker oldInfraNetworkTracker = mInfraNetworkTracker;
@@ -243,7 +232,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
Inet6Address ftdOmr = requireNonNull(ftd.getOmrAddress());
Inet6Address ftdMlEid = requireNonNull(ftd.getMlEid());
@@ -285,7 +274,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_5);
@@ -307,7 +296,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_3);
@@ -328,7 +317,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd, GROUP_ADDR_SCOPE_5);
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -360,7 +349,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_3);
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_3);
@@ -382,7 +371,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_4);
@@ -407,11 +396,11 @@
*/
FullThreadDevice ftd1 = mFtds.get(0);
- startFtdChild(ftd1);
+ joinNetworkAndWaitForOmr(ftd1, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd1, GROUP_ADDR_SCOPE_5);
FullThreadDevice ftd2 = mFtds.get(1);
- startFtdChild(ftd2);
+ joinNetworkAndWaitForOmr(ftd2, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd2, GROUP_ADDR_SCOPE_4);
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -443,11 +432,11 @@
*/
FullThreadDevice ftd1 = mFtds.get(0);
- startFtdChild(ftd1);
+ joinNetworkAndWaitForOmr(ftd1, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd1, GROUP_ADDR_SCOPE_5);
FullThreadDevice ftd2 = mFtds.get(1);
- startFtdChild(ftd2);
+ joinNetworkAndWaitForOmr(ftd2, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd2, GROUP_ADDR_SCOPE_5);
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -473,7 +462,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
Inet6Address ftdOmr = ftd.getOmrAddress();
ftd.ping(GROUP_ADDR_SCOPE_5);
@@ -499,7 +488,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.ping(GROUP_ADDR_SCOPE_3);
@@ -521,7 +510,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
Inet6Address ftdLla = ftd.getLinkLocalAddress();
assertNotNull(ftdLla);
@@ -544,7 +533,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
List<Inet6Address> ftdMlas = ftd.getMeshLocalAddresses();
assertFalse(ftdMlas.isEmpty());
@@ -571,7 +560,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
subscribeMulticastAddressAndWait(ftd, GROUP_ADDR_SCOPE_5);
Inet6Address ftdOmr = ftd.getOmrAddress();
@@ -599,7 +588,7 @@
*/
FullThreadDevice ftd = mFtds.get(0);
- startFtdChild(ftd);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
Inet6Address ftdOmr = ftd.getOmrAddress();
// Destroy infra link and re-create
@@ -629,15 +618,6 @@
runAsShell(MANAGE_TEST_NETWORKS, () -> mInfraNetworkTracker.teardown());
}
- private void startFtdChild(FullThreadDevice ftd) throws Exception {
- ftd.factoryReset();
- ftd.joinNetwork(DEFAULT_DATASET);
- ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
- waitFor(() -> ftd.getOmrAddress() != null, Duration.ofSeconds(60));
- Inet6Address ftdOmr = ftd.getOmrAddress();
- assertNotNull(ftdOmr);
- }
-
private void startInfraDeviceAndWaitForOnLinkAddr() throws Exception {
mInfraDevice =
new InfraNetworkDevice(MacAddress.fromString("1:2:3:4:5:6"), mInfraNetworkReader);
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index e10f134..2afca5f 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -18,10 +18,10 @@
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.nsd.NsdManager.PROTOCOL_DNS_SD;
-import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.discoverForServiceLost;
import static android.net.thread.utils.IntegrationTestUtils.discoverService;
+import static android.net.thread.utils.IntegrationTestUtils.joinNetworkAndWaitForOmr;
import static android.net.thread.utils.IntegrationTestUtils.resolveService;
import static android.net.thread.utils.IntegrationTestUtils.resolveServiceUntil;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
@@ -168,8 +168,7 @@
// Creates Full Thread Devices (FTD) and let them join the network.
for (FullThreadDevice ftd : mFtds) {
- ftd.joinNetwork(DEFAULT_DATASET);
- ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
}
int randomId = new Random().nextInt(10_000);
@@ -223,8 +222,7 @@
// Creates a Full Thread Devices (FTD) and let it join the network.
FullThreadDevice ftd = mFtds.get(0);
- ftd.joinNetwork(DEFAULT_DATASET);
- ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.setSrpHostname("my-host");
ftd.setSrpHostAddresses(List.of((Inet6Address) parseNumericAddress("2001:db8::1")));
ftd.addSrpService(
@@ -279,8 +277,7 @@
// Creates a Full Thread Devices (FTD) and let it join the network.
FullThreadDevice ftd = mFtds.get(0);
- ftd.joinNetwork(DEFAULT_DATASET);
- ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.setSrpHostname("my-host");
ftd.setSrpHostAddresses(
List.of(
@@ -346,8 +343,7 @@
mRegistrationListeners.add(listener);
for (int i = 0; i < NUM_FTD; ++i) {
FullThreadDevice ftd = mFtds.get(i);
- ftd.joinNetwork(DEFAULT_DATASET);
- ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
ftd.setDnsServerAddress(mOtCtl.getMlEid().getHostAddress());
}
final ArrayList<NsdServiceInfo> browsedServices = new ArrayList<>();
@@ -409,8 +405,7 @@
* </pre>
*/
FullThreadDevice srpClient = mFtds.get(0);
- srpClient.joinNetwork(DEFAULT_DATASET);
- srpClient.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(srpClient, DEFAULT_DATASET);
srpClient.setSrpHostname("my-host");
srpClient.setSrpHostAddresses(List.of((Inet6Address) parseNumericAddress("2001::1")));
srpClient.addSrpService(
@@ -421,8 +416,7 @@
Map.of("key1", bytes(0x01, 0x02), "key2", bytes(0x03)));
FullThreadDevice dnsClient = mFtds.get(1);
- dnsClient.joinNetwork(DEFAULT_DATASET);
- dnsClient.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ joinNetworkAndWaitForOmr(dnsClient, DEFAULT_DATASET);
dnsClient.setDnsServerAddress(mOtCtl.getMlEid().getHostAddress());
NsdServiceInfo browsedService = dnsClient.browseService("_test._udp.default.service.arpa.");
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 c0a8eea..8440bbc 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -282,6 +282,7 @@
for (String subtype : subtypes) {
fullServiceType.append(",").append(subtype);
}
+ waitForSrpServer();
executeCommand(
"srp client service add %s %s %d %d %d %s",
serviceName,
@@ -492,6 +493,22 @@
return -1;
}
+ /** Waits for an SRP server to be present in Network Data */
+ private void waitForSrpServer() throws TimeoutException {
+ // CLI output:
+ // > srp client server
+ // [fd64:db12:25f4:7e0b:1bfc:6344:25ac:2dd7]:53538
+ // Done
+ waitFor(
+ () -> {
+ final String serverAddr = executeCommand("srp client server").get(0);
+ final int lastColonIndex = serverAddr.lastIndexOf(':');
+ final int port = Integer.parseInt(serverAddr.substring(lastColonIndex + 1));
+ return port > 0;
+ },
+ SERVICE_DISCOVERY_TIMEOUT);
+ }
+
@FormatMethod
private List<String> executeCommand(String commandFormat, Object... args) {
return executeCommand(String.format(commandFormat, args));
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 ada46c8..7b0c415 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -22,8 +22,11 @@
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static org.junit.Assert.assertNotNull;
+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -36,6 +39,7 @@
import android.net.TestNetworkInterface;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.net.thread.ActiveOperationalDataset;
import android.net.thread.ThreadNetworkController;
import android.os.Build;
import android.os.Handler;
@@ -84,6 +88,17 @@
public static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1);
public static final Duration SERVICE_DISCOVERY_TIMEOUT = Duration.ofSeconds(20);
+ // A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
+ private static final byte[] DEFAULT_DATASET_TLVS =
+ base16().decode(
+ "0E080000000000010000000300001335060004001FFFE002"
+ + "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
+ + "53760F519A63BAFDDFFC80D2AF030F4F70656E5468726561"
+ + "642D643961300102D9A00410A245479C836D551B9CA557F7"
+ + "B9D351B40C0402A0FFF8");
+ public static final ActiveOperationalDataset DEFAULT_DATASET =
+ ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
+
private IntegrationTestUtils() {}
/**
@@ -413,6 +428,22 @@
return networkFuture.get(timeout.toSeconds(), SECONDS);
}
+ /**
+ * Let the FTD join the specified Thread network and wait for border routing to be available.
+ *
+ * @return the OMR address
+ */
+ public static Inet6Address joinNetworkAndWaitForOmr(
+ FullThreadDevice ftd, ActiveOperationalDataset dataset) throws Exception {
+ ftd.factoryReset();
+ ftd.joinNetwork(dataset);
+ ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+ waitFor(() -> ftd.getOmrAddress() != null, Duration.ofSeconds(60));
+ Inet6Address ftdOmr = ftd.getOmrAddress();
+ assertNotNull(ftdOmr);
+ return ftdOmr;
+ }
+
private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener {
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {}
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 6e2369f..a5dc25a 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -26,7 +26,6 @@
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
-import static com.android.server.thread.ThreadPersistentSettings.THREAD_ENABLED_IN_AIRPLANE_MODE;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_INVALID_STATE;
import static com.google.common.io.BaseEncoding.base16;
@@ -49,6 +48,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -68,7 +69,6 @@
import android.os.SystemClock;
import android.os.UserManager;
import android.os.test.TestLooper;
-import android.provider.Settings;
import android.util.AtomicFile;
import androidx.test.annotation.UiThreadTest;
@@ -96,11 +96,11 @@
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
-import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
@@ -150,7 +150,6 @@
private static final byte[] TEST_VENDOR_OUI_BYTES = new byte[] {(byte) 0xAC, (byte) 0xDE, 0x48};
private static final String TEST_VENDOR_NAME = "test vendor";
private static final String TEST_MODEL_NAME = "test model";
- private static final boolean TEST_VGH_VALUE = false;
@Mock private ConnectivityManager mMockConnectivityManager;
@Mock private NetworkAgent mMockNetworkAgent;
@@ -193,8 +192,6 @@
when(mMockUserManager.hasUserRestriction(eq(DISALLOW_THREAD_NETWORK))).thenReturn(false);
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
-
when(mConnectivityResources.get()).thenReturn(mResources);
when(mResources.getBoolean(eq(R.bool.config_thread_default_enabled))).thenReturn(true);
when(mResources.getString(eq(R.string.config_thread_vendor_name)))
@@ -203,8 +200,8 @@
.thenReturn(TEST_VENDOR_OUI);
when(mResources.getString(eq(R.string.config_thread_model_name)))
.thenReturn(TEST_MODEL_NAME);
- when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
- .thenReturn(TEST_VGH_VALUE);
+ when(mResources.getStringArray(eq(R.array.config_thread_mdns_vendor_specific_txts)))
+ .thenReturn(new String[] {});
final AtomicFile storageFile = new AtomicFile(tempFolder.newFile("thread_settings.xml"));
mPersistentSettings = new ThreadPersistentSettings(storageFile, mConnectivityResources);
@@ -247,8 +244,8 @@
.thenReturn(TEST_VENDOR_OUI);
when(mResources.getString(eq(R.string.config_thread_model_name)))
.thenReturn(TEST_MODEL_NAME);
- when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
- .thenReturn(true);
+ when(mResources.getStringArray(eq(R.array.config_thread_mdns_vendor_specific_txts)))
+ .thenReturn(new String[] {"vt=test"});
mService.initialize();
mTestLooper.dispatchAll();
@@ -258,19 +255,7 @@
assertThat(meshcopTxts.vendorOui).isEqualTo(TEST_VENDOR_OUI_BYTES);
assertThat(meshcopTxts.modelName).isEqualTo(TEST_MODEL_NAME);
assertThat(meshcopTxts.nonStandardTxtEntries)
- .containsExactly(new DnsTxtAttribute("vgh", "1".getBytes(StandardCharsets.UTF_8)));
- }
-
- @Test
- public void getMeshcopTxtAttributes_managedByGoogleIsFalse_vghIsZero() {
- when(mResources.getBoolean(eq(R.bool.config_thread_managed_by_google_home)))
- .thenReturn(false);
-
- MeshcopTxtAttributes meshcopTxts =
- ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources);
-
- assertThat(meshcopTxts.nonStandardTxtEntries)
- .containsExactly(new DnsTxtAttribute("vgh", "0".getBytes(StandardCharsets.UTF_8)));
+ .containsExactly(new DnsTxtAttribute("vt", "test".getBytes(UTF_8)));
}
@Test
@@ -343,6 +328,61 @@
}
@Test
+ public void makeVendorSpecificTxtAttrs_validTxts_returnsParsedTxtAttrs() {
+ String[] txts = new String[] {"va=123", "vb=", "vc"};
+
+ List<DnsTxtAttribute> attrs = mService.makeVendorSpecificTxtAttrs(txts);
+
+ assertThat(attrs)
+ .containsExactly(
+ new DnsTxtAttribute("va", "123".getBytes(UTF_8)),
+ new DnsTxtAttribute("vb", new byte[] {}),
+ new DnsTxtAttribute("vc", new byte[] {}));
+ }
+
+ @Test
+ public void makeVendorSpecificTxtAttrs_txtKeyNotStartWithV_throwsIllegalArgument() {
+ String[] txts = new String[] {"abc=123"};
+
+ assertThrows(
+ IllegalArgumentException.class, () -> mService.makeVendorSpecificTxtAttrs(txts));
+ }
+
+ @Test
+ public void makeVendorSpecificTxtAttrs_txtIsTooShort_throwsIllegalArgument() {
+ String[] txtEmptyKey = new String[] {"=123"};
+ String[] txtSingleCharKey = new String[] {"v=456"};
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mService.makeVendorSpecificTxtAttrs(txtEmptyKey));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mService.makeVendorSpecificTxtAttrs(txtSingleCharKey));
+ }
+
+ @Test
+ public void makeVendorSpecificTxtAttrs_txtValueIsEmpty_parseSuccess() {
+ String[] txts = new String[] {"va=", "vb"};
+
+ List<DnsTxtAttribute> attrs = mService.makeVendorSpecificTxtAttrs(txts);
+
+ assertThat(attrs)
+ .containsExactly(
+ new DnsTxtAttribute("va", new byte[] {}),
+ new DnsTxtAttribute("vb", new byte[] {}));
+ }
+
+ @Test
+ public void makeVendorSpecificTxtAttrs_multipleEquals_splittedByTheFirstEqual() {
+ String[] txts = new String[] {"va=abc=def=123"};
+
+ List<DnsTxtAttribute> attrs = mService.makeVendorSpecificTxtAttrs(txts);
+
+ assertThat(attrs).containsExactly(new DnsTxtAttribute("va", "abc=def=123".getBytes(UTF_8)));
+ }
+
+ @Test
public void join_otDaemonRemoteFailure_returnsInternalError() throws Exception {
mService.initialize();
final IOperationReceiver mockReceiver = mock(IOperationReceiver.class);
@@ -437,100 +477,6 @@
assertThat(failure.getErrorCode()).isEqualTo(ERROR_FAILED_PRECONDITION);
}
- @Test
- public void airplaneMode_initWithAirplaneModeOn_otDaemonNotStarted() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
-
- mService.initialize();
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.isInitialized()).isFalse();
- }
-
- @Test
- public void airplaneMode_initWithAirplaneModeOff_threadIsEnabled() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
-
- mService.initialize();
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(STATE_ENABLED);
- }
-
- @Test
- public void airplaneMode_changesFromOffToOn_stateIsDisabled() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
- AtomicReference<BroadcastReceiver> receiverRef =
- captureBroadcastReceiver(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mService.initialize();
- mTestLooper.dispatchAll();
-
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(STATE_DISABLED);
- }
-
- @Test
- public void airplaneMode_changesFromOnToOff_stateIsEnabled() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- AtomicReference<BroadcastReceiver> receiverRef =
- captureBroadcastReceiver(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mService.initialize();
- mTestLooper.dispatchAll();
-
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(STATE_ENABLED);
- }
-
- @Test
- public void airplaneMode_setEnabledWhenAirplaneModeIsOn_WillNotAutoDisableSecondTime() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- AtomicReference<BroadcastReceiver> receiverRef =
- captureBroadcastReceiver(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- CompletableFuture<Void> setEnabledFuture = new CompletableFuture<>();
- mService.initialize();
-
- mService.setEnabled(true, newOperationReceiver(setEnabledFuture));
- mTestLooper.dispatchAll();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(STATE_ENABLED);
- assertThat(mPersistentSettings.get(THREAD_ENABLED_IN_AIRPLANE_MODE)).isTrue();
- }
-
- @Test
- public void airplaneMode_setDisabledWhenAirplaneModeIsOn_WillAutoDisableSecondTime() {
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- AtomicReference<BroadcastReceiver> receiverRef =
- captureBroadcastReceiver(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- CompletableFuture<Void> setEnabledFuture = new CompletableFuture<>();
- mService.initialize();
- mService.setEnabled(true, newOperationReceiver(setEnabledFuture));
- mTestLooper.dispatchAll();
-
- mService.setEnabled(false, newOperationReceiver(setEnabledFuture));
- mTestLooper.dispatchAll();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
- receiverRef.get().onReceive(mContext, new Intent());
- mTestLooper.dispatchAll();
-
- assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(STATE_DISABLED);
- assertThat(mPersistentSettings.get(THREAD_ENABLED_IN_AIRPLANE_MODE)).isFalse();
- }
-
private AtomicReference<BroadcastReceiver> captureBroadcastReceiver(String action) {
AtomicReference<BroadcastReceiver> receiverRef = new AtomicReference<>();
@@ -542,7 +488,9 @@
.when(mContext)
.registerReceiver(
any(BroadcastReceiver.class),
- argThat(actualIntentFilter -> actualIntentFilter.hasAction(action)));
+ argThat(actualIntentFilter -> actualIntentFilter.hasAction(action)),
+ any(),
+ any());
return receiverRef;
}
@@ -768,12 +716,12 @@
ThreadConfiguration config1 =
new ThreadConfiguration.Builder()
.setNat64Enabled(false)
- .setDhcp6PdEnabled(false)
+ .setDhcpv6PdEnabled(false)
.build();
ThreadConfiguration config2 =
new ThreadConfiguration.Builder()
.setNat64Enabled(true)
- .setDhcp6PdEnabled(true)
+ .setDhcpv6PdEnabled(true)
.build();
ThreadConfiguration config3 =
new ThreadConfiguration.Builder(config2).build(); // Same as config2
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadPersistentSettingsTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadPersistentSettingsTest.java
index c932ac8..ba489d9 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadPersistentSettingsTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadPersistentSettingsTest.java
@@ -152,7 +152,7 @@
ThreadConfiguration configuration =
new ThreadConfiguration.Builder()
.setNat64Enabled(true)
- .setDhcp6PdEnabled(true)
+ .setDhcpv6PdEnabled(true)
.build();
mThreadPersistentSettings.putConfiguration(configuration);
@@ -164,13 +164,13 @@
ThreadConfiguration configuration1 =
new ThreadConfiguration.Builder()
.setNat64Enabled(false)
- .setDhcp6PdEnabled(false)
+ .setDhcpv6PdEnabled(false)
.build();
mThreadPersistentSettings.putConfiguration(configuration1);
ThreadConfiguration configuration2 =
new ThreadConfiguration.Builder()
.setNat64Enabled(true)
- .setDhcp6PdEnabled(true)
+ .setDhcpv6PdEnabled(true)
.build();
assertThat(mThreadPersistentSettings.putConfiguration(configuration2)).isTrue();
@@ -188,9 +188,9 @@
}
@Test
- public void putConfiguration_dhcp6PdEnabled_valuesUpdatedAndPersisted() throws Exception {
+ public void putConfiguration_dhcpv6PdEnabled_valuesUpdatedAndPersisted() throws Exception {
ThreadConfiguration configuration =
- new ThreadConfiguration.Builder().setDhcp6PdEnabled(true).build();
+ new ThreadConfiguration.Builder().setDhcpv6PdEnabled(true).build();
mThreadPersistentSettings.putConfiguration(configuration);
assertThat(mThreadPersistentSettings.getConfiguration()).isEqualTo(configuration);