Merge mainline-release 6664920 to master - DO NOT MERGE
Merged-In: I09503c3e461299afc4d58c69d235369857797d9b
Change-Id: Iec3c6d656851a8ed3e49a1b9d406754c90b7881c
diff --git a/Android.bp b/Android.bp
index 37e001c..314def9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -87,7 +87,7 @@
libs: ["unsupportedappusage"],
static_libs: [
"androidx.annotation_annotation",
- "netd_aidl_interface-java",
+ "netd_aidl_interface-unstable-java",
"netlink-client",
"networkstack-client",
"net-utils-framework-common",
@@ -95,6 +95,7 @@
"datastallprotosnano",
"statsprotos",
"captiveportal-lib",
+ "net-utils-device-common",
],
plugins: ["java_api_finder"],
}
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 195935d..b61c89f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,7 +25,7 @@
<!-- Permissions must be defined here, and not in the base manifest, as the network stack
running in the system server process does not need any permission, and having privileged
permissions added would cause crashes on startup unless they are also added to the
- privileged permissions whitelist for that package. -->
+ privileged permissions allowlist for that package. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
@@ -44,11 +44,23 @@
android:persistent="true"
android:process="com.android.networkstack.process">
<service android:name="com.android.server.NetworkStackService"
+ android:exported="true"
android:permission="android.permission.MAINLINE_NETWORK_STACK">
<intent-filter>
<action android:name="android.net.INetworkStackConnector"/>
</intent-filter>
</service>
+ <!-- Test instrumentation service, only usable on debuggable builds.
+ The service is protected by NETWORK_SETTINGS permissions as there is no better
+ networking-related permission that exists on Q, is sufficiently protected (signature),
+ and can be obtained via shell permissions. -->
+ <service android:name="com.android.server.TestNetworkStackService"
+ android:permission="android.permission.NETWORK_SETTINGS"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.net.INetworkStackConnector.Test"/>
+ </intent-filter>
+ </service>
<service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/AndroidManifest_InProcess.xml b/AndroidManifest_InProcess.xml
index 41b2e86..2cb146a 100644
--- a/AndroidManifest_InProcess.xml
+++ b/AndroidManifest_InProcess.xml
@@ -23,6 +23,7 @@
<application>
<service android:name="com.android.server.NetworkStackService"
android:process="system"
+ android:exported="true"
android:permission="android.permission.MAINLINE_NETWORK_STACK">
<intent-filter>
<action android:name="android.net.INetworkStackConnector.InProcess"/>
diff --git a/apishim/common/com/android/networkstack/apishim/common/ShimUtils.java b/apishim/common/com/android/networkstack/apishim/common/ShimUtils.java
index d78cd0c..648751b 100644
--- a/apishim/common/com/android/networkstack/apishim/common/ShimUtils.java
+++ b/apishim/common/com/android/networkstack/apishim/common/ShimUtils.java
@@ -49,4 +49,11 @@
public static boolean isAtLeastR() {
return isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q);
}
+
+ /**
+ * Check whether the device supports in-development or final S networking APIs.
+ */
+ public static boolean isAtLeastS() {
+ return isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R);
+ }
}
diff --git a/common/captiveportal/Android.bp b/common/captiveportal/Android.bp
index d34ab85..0b49eb2 100644
--- a/common/captiveportal/Android.bp
+++ b/common/captiveportal/Android.bp
@@ -21,4 +21,6 @@
"androidx.annotation_annotation",
],
sdk_version: "system_current",
+ // this is part of updatable modules(NetworkStack) which targets 29(Q)
+ min_sdk_version: "29",
}
\ No newline at end of file
diff --git a/common/moduleutils/Android.bp b/common/moduleutils/Android.bp
index 9dfec42..0e78212 100644
--- a/common/moduleutils/Android.bp
+++ b/common/moduleutils/Android.bp
@@ -56,22 +56,8 @@
"src/android/net/netlink/*.java",
"src/android/net/shared/NetdUtils.java",
"src/android/net/shared/RouteUtils.java",
- "src/android/net/util/FdEventsReader.java",
"src/android/net/util/InterfaceParams.java",
- "src/android/net/util/PacketReader.java",
- "src/android/net/util/SharedLog.java"
+ "src/android/net/util/SharedLog.java",
],
visibility: ["//frameworks/base/packages/Tethering"],
}
-
-// Utility sources used by test libraries.
-// This is its own group to limit indiscriminate dependency of test code on production code.
-// TODO: move these classes and NetworkStack/tests/lib to frameworks/libs/net, and remove this.
-filegroup {
- name: "net-module-utils-srcs-for-tests",
- visibility: ["//packages/modules/NetworkStack/tests/lib"],
- srcs: [
- "src/android/net/util/FdEventsReader.java",
- "src/android/net/util/PacketReader.java",
- ],
-}
diff --git a/common/moduleutils/src/android/net/util/FdEventsReader.java b/common/moduleutils/src/android/net/util/FdEventsReader.java
deleted file mode 100644
index 9045c01..0000000
--- a/common/moduleutils/src/android/net/util/FdEventsReader.java
+++ /dev/null
@@ -1,267 +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 android.net.util;
-
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * This class encapsulates the mechanics of registering a file descriptor
- * with a thread's Looper and handling read events (and errors).
- *
- * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
- * onStop() and onStart().
- *
- * Subclasses can expect a call life-cycle like the following:
- *
- * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
- * goes well. Implementations may override onStart() for additional initialization.
- *
- * [2] yield, waiting for read event or error notification:
- *
- * [a] readPacket() && handlePacket()
- *
- * [b] if (no error):
- * goto 2
- * else:
- * goto 3
- *
- * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
- * started). Implementations may override onStop() for additional cleanup.
- *
- * The packet receive buffer is recycled on every read call, so subclasses
- * should make any copies they would like inside their handlePacket()
- * implementation.
- *
- * All public methods MUST only be called from the same thread with which
- * the Handler constructor argument is associated.
- *
- * @param <BufferType> the type of the buffer used to read data.
- */
-public abstract class FdEventsReader<BufferType> {
- private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
- private static final int UNREGISTER_THIS_FD = 0;
-
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final MessageQueue mQueue;
- @NonNull
- private final BufferType mBuffer;
- @Nullable
- private FileDescriptor mFd;
- private long mPacketsReceived;
-
- protected static void closeFd(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
- mHandler = h;
- mQueue = mHandler.getLooper().getQueue();
- mBuffer = buffer;
- }
-
- @VisibleForTesting
- @NonNull
- protected MessageQueue getMessageQueue() {
- return mQueue;
- }
-
- /** Start this FdEventsReader. */
- public boolean start() {
- if (!onCorrectThread()) {
- throw new IllegalStateException("start() called from off-thread");
- }
-
- return createAndRegisterFd();
- }
-
- /** Stop this FdEventsReader and destroy the file descriptor. */
- public void stop() {
- if (!onCorrectThread()) {
- throw new IllegalStateException("stop() called from off-thread");
- }
-
- unregisterAndDestroyFd();
- }
-
- @NonNull
- public Handler getHandler() {
- return mHandler;
- }
-
- protected abstract int recvBufSize(@NonNull BufferType buffer);
-
- /** Returns the size of the receive buffer. */
- public int recvBufSize() {
- return recvBufSize(mBuffer);
- }
-
- /**
- * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}.
- *
- * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0.
- */
- public final long numPacketsReceived() {
- return mPacketsReceived;
- }
-
- /**
- * Subclasses MUST create the listening socket here, including setting all desired socket
- * options, interface or address/port binding, etc. The socket MUST be created nonblocking.
- */
- @Nullable
- protected abstract FileDescriptor createFd();
-
- /**
- * Implementations MUST return the bytes read or throw an Exception.
- *
- * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or
- * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer
- * contents and respectively wait for further input or retry the read immediately. For all other
- * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this
- * method.
- */
- protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
- throws Exception;
-
- /**
- * Called by the main loop for every packet. Any desired copies of
- * |recvbuf| should be made in here, as the underlying byte array is
- * reused across all reads.
- */
- protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
-
- /**
- * Called by the main loop to log errors. In some cases |e| may be null.
- */
- protected void logError(@NonNull String msg, @Nullable Exception e) {}
-
- /**
- * Called by start(), if successful, just prior to returning.
- */
- protected void onStart() {}
-
- /**
- * Called by stop() just prior to returning.
- */
- protected void onStop() {}
-
- private boolean createAndRegisterFd() {
- if (mFd != null) return true;
-
- try {
- mFd = createFd();
- } catch (Exception e) {
- logError("Failed to create socket: ", e);
- closeFd(mFd);
- mFd = null;
- }
-
- if (mFd == null) return false;
-
- getMessageQueue().addOnFileDescriptorEventListener(
- mFd,
- FD_EVENTS,
- (fd, events) -> {
- // Always call handleInput() so read/recvfrom are given
- // a proper chance to encounter a meaningful errno and
- // perhaps log a useful error message.
- if (!isRunning() || !handleInput()) {
- unregisterAndDestroyFd();
- return UNREGISTER_THIS_FD;
- }
- return FD_EVENTS;
- });
- onStart();
- return true;
- }
-
- private boolean isRunning() {
- return (mFd != null) && mFd.valid();
- }
-
- // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
- private boolean handleInput() {
- while (isRunning()) {
- final int bytesRead;
-
- try {
- bytesRead = readPacket(mFd, mBuffer);
- if (bytesRead < 1) {
- if (isRunning()) logError("Socket closed, exiting", null);
- break;
- }
- mPacketsReceived++;
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.EAGAIN) {
- // We've read everything there is to read this time around.
- return true;
- } else if (e.errno == OsConstants.EINTR) {
- continue;
- } else {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
- } catch (Exception e) {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
-
- try {
- handlePacket(mBuffer, bytesRead);
- } catch (Exception e) {
- logError("handlePacket error: ", e);
- Log.wtf(FdEventsReader.class.getSimpleName(), "Error handling packet", e);
- }
- }
-
- return false;
- }
-
- private void unregisterAndDestroyFd() {
- if (mFd == null) return;
-
- getMessageQueue().removeOnFileDescriptorEventListener(mFd);
- closeFd(mFd);
- mFd = null;
- onStop();
- }
-
- private boolean onCorrectThread() {
- return (mHandler.getLooper() == Looper.myLooper());
- }
-}
diff --git a/common/moduleutils/src/android/net/util/PacketReader.java b/common/moduleutils/src/android/net/util/PacketReader.java
deleted file mode 100644
index 0be7187..0000000
--- a/common/moduleutils/src/android/net/util/PacketReader.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static java.lang.Math.max;
-
-import android.os.Handler;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-
-/**
- * Specialization of {@link FdEventsReader} that reads packets into a byte array.
- *
- * TODO: rename this class to something more correctly descriptive (something
- * like [or less horrible than] FdReadEventsHandler?).
- */
-public abstract class PacketReader extends FdEventsReader<byte[]> {
-
- public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
-
- protected PacketReader(Handler h) {
- this(h, DEFAULT_RECV_BUF_SIZE);
- }
-
- protected PacketReader(Handler h, int recvBufSize) {
- super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
- }
-
- @Override
- protected final int recvBufSize(byte[] buffer) {
- return buffer.length;
- }
-
- /**
- * Subclasses MAY override this to change the default read() implementation
- * in favour of, say, recvfrom().
- *
- * Implementations MUST return the bytes read or throw an Exception.
- */
- @Override
- protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
- return Os.read(fd, packetBuffer, 0, packetBuffer.length);
- }
-}
diff --git a/common/netlinkclient/Android.bp b/common/netlinkclient/Android.bp
index a61cdbd..2b4a2d6 100644
--- a/common/netlinkclient/Android.bp
+++ b/common/netlinkclient/Android.bp
@@ -24,4 +24,6 @@
"androidx.annotation_annotation",
],
sdk_version: "system_current",
+ // this is part of updatable modules(NetworkStack) which targets 29(Q)
+ min_sdk_version: "29",
}
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp
index b5a3e95..a86f7fd 100644
--- a/common/networkstackclient/Android.bp
+++ b/common/networkstackclient/Android.bp
@@ -28,7 +28,11 @@
apex_available: [
"//apex_available:platform",
"com.android.wifi",
+ "com.android.bluetooth.updatable",
+ "com.android.tethering",
],
+ // this is part of updatable modules(NetworkStack) which targets 29(Q)
+ min_sdk_version: "29",
},
ndk: {
enabled: false,
@@ -45,6 +49,7 @@
"5",
"6",
"7",
+ "8",
],
visibility: [
"//system/tools/aidl/build",
@@ -56,7 +61,7 @@
local_include_dir: "src",
include_dirs: [
"frameworks/base/core/java", // For framework parcelables.
- "frameworks/native/aidl/binder/android/os", // For PersistableBundle.aidl
+ "frameworks/native/aidl/binder", // For PersistableBundle.aidl
"frameworks/base/wifi/aidl-export", // For wifi parcelables.
],
srcs: [
@@ -83,6 +88,8 @@
"src/android/net/dhcp/IDhcpServerCallbacks.aidl",
"src/android/net/ip/IIpClient.aidl",
"src/android/net/ip/IIpClientCallbacks.aidl",
+ // New AIDL classes should go into android.net.networkstack.aidl so they can be clearly
+ // identified
],
backend: {
java: {
@@ -90,7 +97,10 @@
"//apex_available:platform",
"com.android.bluetooth.updatable",
"com.android.wifi",
+ "com.android.tethering",
],
+ // this is part of updatable modules(NetworkStack) which targets 29(Q)
+ min_sdk_version: "29",
},
ndk: {
enabled: false,
@@ -108,6 +118,7 @@
"5",
"6",
"7",
+ "8",
],
// TODO: have tethering depend on networkstack-client and set visibility to private
visibility: [
@@ -119,6 +130,8 @@
java_library {
name: "networkstack-client",
sdk_version: "system_current",
+ // this is part of updatable modules(NetworkStack) which targets 29(Q)
+ min_sdk_version: "29",
srcs: [
":framework-annotations",
"src/android/net/IpMemoryStoreClient.java",
@@ -127,8 +140,8 @@
"src/android/net/shared/**/*.java",
],
static_libs: [
- "ipmemorystore-aidl-interfaces-java",
- "networkstack-aidl-interfaces-java",
+ "ipmemorystore-aidl-interfaces-unstable-java",
+ "networkstack-aidl-interfaces-unstable-java",
],
visibility: [
"//frameworks/base/packages/Tethering",
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/.hash b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/.hash
new file mode 100644
index 0000000..346969a
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/.hash
@@ -0,0 +1 @@
+70cbb9e5b4009a86a81aa2a9bdf25e21442224be
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStore.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStore.aidl
new file mode 100644
index 0000000..bf7a26d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStore.aidl
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface IIpMemoryStore {
+ oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
+ oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
+ oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
+ oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
+ oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
+ oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
+ oneway void factoryReset();
+ oneway void delete(String l2Key, boolean needWipe, android.net.ipmemorystore.IOnStatusAndCountListener listener);
+ oneway void deleteCluster(String cluster, boolean needWipe, android.net.ipmemorystore.IOnStatusAndCountListener listener);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStoreCallbacks.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStoreCallbacks.aidl
new file mode 100644
index 0000000..2024391
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/IIpMemoryStoreCallbacks.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface IIpMemoryStoreCallbacks {
+ oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/Blob.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/Blob.aidl
new file mode 100644
index 0000000..8a1b57e
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/Blob.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+parcelable Blob {
+ byte[] data;
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644
index 0000000..e711272
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnBlobRetrievedListener {
+ oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644
index 0000000..4abecb9
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnL2KeyResponseListener {
+ oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
new file mode 100644
index 0000000..05c48b3
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnNetworkAttributesRetrievedListener {
+ oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
new file mode 100644
index 0000000..0bc8c5e
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnSameL3NetworkResponseListener {
+ oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusAndCountListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusAndCountListener.aidl
new file mode 100644
index 0000000..cf30fa1
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusAndCountListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnStatusAndCountListener {
+ oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status, int count);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusListener.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644
index 0000000..e71de47
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/IOnStatusListener.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+interface IOnStatusListener {
+ oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644
index 0000000..92a570d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+parcelable NetworkAttributesParcelable {
+ byte[] assignedV4Address;
+ long assignedV4AddressExpiry;
+ String cluster;
+ android.net.ipmemorystore.Blob[] dnsAddresses;
+ int mtu;
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644
index 0000000..eca0987
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+parcelable SameL3NetworkResponseParcelable {
+ String l2Key1;
+ String l2Key2;
+ float confidence;
+}
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/StatusParcelable.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644
index 0000000..7554608
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/8/android/net/ipmemorystore/StatusParcelable.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ipmemorystore;
+/* @hide */
+parcelable StatusParcelable {
+ int resultCode;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/.hash
new file mode 100644
index 0000000..c7ab51a
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/.hash
@@ -0,0 +1 @@
+f6e2137b5033902774f78726d429399db3b18cab
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DataStallReportParcelable.aidl
new file mode 100644
index 0000000..69ff31f
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DataStallReportParcelable.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable DataStallReportParcelable {
+ long timestampMillis = 0;
+ int detectionMethod = 1;
+ int tcpPacketFailRate = 2;
+ int tcpMetricsCollectionPeriodMillis = 3;
+ int dnsConsecutiveTimeouts = 4;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DhcpResultsParcelable.aidl
new file mode 100644
index 0000000..7bb5c41
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable DhcpResultsParcelable {
+ android.net.StaticIpConfiguration baseConfiguration;
+ int leaseDuration;
+ int mtu;
+ String serverAddress;
+ String vendorInfo;
+ @nullable String serverHostName;
+ @nullable String captivePortalApiUrl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..5945819
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitor.aidl
@@ -0,0 +1,42 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetworkMonitor {
+ oneway void start();
+ oneway void launchCaptivePortalApp();
+ oneway void notifyCaptivePortalAppFinished(int response);
+ oneway void setAcceptPartialConnectivity();
+ oneway void forceReevaluation(int uid);
+ oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
+ oneway void notifyDnsResponse(int returnCode);
+ oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
+ oneway void notifyNetworkDisconnected();
+ oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
+ oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
+ const int NETWORK_TEST_RESULT_VALID = 0;
+ const int NETWORK_TEST_RESULT_INVALID = 1;
+ const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
+ const int NETWORK_VALIDATION_RESULT_VALID = 1;
+ const int NETWORK_VALIDATION_RESULT_PARTIAL = 2;
+ const int NETWORK_VALIDATION_PROBE_DNS = 4;
+ const int NETWORK_VALIDATION_PROBE_HTTP = 8;
+ const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
+ const int NETWORK_VALIDATION_PROBE_FALLBACK = 32;
+ const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..b7ddad9
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetworkMonitorCallbacks {
+ oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor) = 0;
+ oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl) = 1;
+ oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config) = 2;
+ oneway void showProvisioningNotification(String action, String packageName) = 3;
+ oneway void hideProvisioningNotification() = 4;
+ oneway void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) = 5;
+ oneway void notifyNetworkTestedWithExtras(in android.net.NetworkTestResultParcelable result) = 6;
+ oneway void notifyDataStallSuspected(in android.net.DataStallReportParcelable report) = 7;
+ oneway void notifyCaptivePortalDataChanged(in android.net.CaptivePortalData data) = 8;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackConnector.aidl
new file mode 100644
index 0000000..0864886
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetworkStackConnector {
+ oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
+ oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
+ oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
+ oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
+ oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..ec16def
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetworkStackStatusCallback {
+ oneway void onStatusAvailable(int statusCode);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InformationElementParcelable.aidl
new file mode 100644
index 0000000..c882bf4
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InformationElementParcelable.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable InformationElementParcelable {
+ int id;
+ byte[] payload;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InitialConfigurationParcelable.aidl
new file mode 100644
index 0000000..c91d7a2
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable InitialConfigurationParcelable {
+ android.net.LinkAddress[] ipAddresses;
+ android.net.IpPrefix[] directlyConnectedRoutes;
+ String[] dnsServers;
+ String gateway;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2InformationParcelable.aidl
new file mode 100644
index 0000000..dca5138
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2InformationParcelable.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable Layer2InformationParcelable {
+ String l2Key;
+ String cluster;
+ android.net.MacAddress bssid;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2PacketParcelable.aidl
new file mode 100644
index 0000000..2e0955f
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/Layer2PacketParcelable.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable Layer2PacketParcelable {
+ android.net.MacAddress dstMacAddress;
+ byte[] payload;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NattKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..aa09c3d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable NattKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NetworkTestResultParcelable.aidl
new file mode 100644
index 0000000..f31a669
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/NetworkTestResultParcelable.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable NetworkTestResultParcelable {
+ long timestampMillis;
+ int result;
+ int probesSucceeded;
+ int probesAttempted;
+ String redirectUrl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..cada4d3
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable PrivateDnsConfigParcel {
+ String hostname;
+ String[] ips;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ProvisioningConfigurationParcelable.aidl
new file mode 100644
index 0000000..b8dfb91
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable ProvisioningConfigurationParcelable {
+ boolean enableIPv4;
+ boolean enableIPv6;
+ boolean usingMultinetworkPolicyTracker;
+ boolean usingIpReachabilityMonitor;
+ int requestedPreDhcpActionMs;
+ android.net.InitialConfigurationParcelable initialConfig;
+ android.net.StaticIpConfiguration staticIpConfig;
+ android.net.apf.ApfCapabilities apfCapabilities;
+ int provisioningTimeoutMs;
+ int ipv6AddrGenMode;
+ android.net.Network network;
+ String displayName;
+ boolean enablePreconnection;
+ @nullable android.net.ScanResultInfoParcelable scanResultInfo;
+ @nullable android.net.Layer2InformationParcelable layer2Info;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ScanResultInfoParcelable.aidl
new file mode 100644
index 0000000..f7ac167
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ScanResultInfoParcelable.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable ScanResultInfoParcelable {
+ String ssid;
+ String bssid;
+ android.net.InformationElementParcelable[] informationElements;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/TcpKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..c50f541
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable TcpKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+ int seq;
+ int ack;
+ int rcvWnd;
+ int rcvWndScale;
+ int tos;
+ int ttl;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpLeaseParcelable.aidl
new file mode 100644
index 0000000..adbd57d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpLeaseParcelable.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.dhcp;
+parcelable DhcpLeaseParcelable {
+ byte[] clientId;
+ byte[] hwAddr;
+ int netAddr;
+ int prefixLength;
+ long expTime;
+ String hostname;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..d66ca9d
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,30 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.dhcp;
+parcelable DhcpServingParamsParcel {
+ int serverAddr;
+ int serverAddrPrefixLength;
+ int[] defaultRouters;
+ int[] dnsServers;
+ int[] excludedAddrs;
+ long dhcpLeaseTimeSecs;
+ int linkMtu;
+ boolean metered;
+ int singleClientAddr = 0;
+ boolean changePrefixOnDecline = false;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpEventCallbacks.aidl
new file mode 100644
index 0000000..dfcaf98
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpEventCallbacks.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.dhcp;
+interface IDhcpEventCallbacks {
+ oneway void onLeasesChanged(in List<android.net.dhcp.DhcpLeaseParcelable> newLeases);
+ oneway void onNewPrefixRequest(in android.net.IpPrefix currentPrefix);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..ef936cc
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,29 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.dhcp;
+/* @hide */
+interface IDhcpServer {
+ oneway void start(in android.net.INetworkStackStatusCallback cb) = 0;
+ oneway void startWithCallbacks(in android.net.INetworkStackStatusCallback statusCb, in android.net.dhcp.IDhcpEventCallbacks eventCb) = 3;
+ oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb) = 1;
+ oneway void stop(in android.net.INetworkStackStatusCallback cb) = 2;
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_INVALID_ARGUMENT = 2;
+ const int STATUS_UNKNOWN_ERROR = 3;
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..63b89ad
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.dhcp;
+/* @hide */
+interface IDhcpServerCallbacks {
+ oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..9245954
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClient.aidl
@@ -0,0 +1,36 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ip;
+/* @hide */
+interface IIpClient {
+ oneway void completedPreDhcpAction();
+ oneway void confirmConfiguration();
+ oneway void readPacketFilterComplete(in byte[] data);
+ oneway void shutdown();
+ oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
+ oneway void stop();
+ oneway void setTcpBufferSizes(in String tcpBufferSizes);
+ oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
+ oneway void setMulticastFilter(boolean enabled);
+ oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
+ oneway void removeKeepalivePacketFilter(int slot);
+ oneway void setL2KeyAndGroupHint(in String l2Key, in String cluster);
+ oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt);
+ oneway void notifyPreconnectionComplete(boolean success);
+ oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..9aabb1f
--- /dev/null
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/8/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.ip;
+/* @hide */
+interface IIpClientCallbacks {
+ oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
+ oneway void onPreDhcpAction();
+ oneway void onPostDhcpAction();
+ oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
+ oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
+ oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
+ oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
+ oneway void onReachabilityLost(in String logMsg);
+ oneway void onQuit();
+ oneway void installPacketFilter(in byte[] filter);
+ oneway void startReadPacketFilter();
+ oneway void setFallbackMulticastFilter(boolean enabled);
+ oneway void setNeighborDiscoveryOffload(boolean enable);
+ oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets);
+}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkStackConnector.aidl
index 17a65cf..0864886 100644
--- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkStackConnector.aidl
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkStackConnector.aidl
@@ -22,4 +22,5 @@
oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
+ oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb);
}
diff --git a/common/networkstackclient/src/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/src/android/net/INetworkMonitorCallbacks.aidl
index 79eb95a..b5fd280 100644
--- a/common/networkstackclient/src/android/net/INetworkMonitorCallbacks.aidl
+++ b/common/networkstackclient/src/android/net/INetworkMonitorCallbacks.aidl
@@ -21,6 +21,7 @@
import android.net.INetworkMonitor;
import android.net.NetworkTestResultParcelable;
import android.net.PrivateDnsConfigParcel;
+import android.os.PersistableBundle;
/** @hide */
oneway interface INetworkMonitorCallbacks {
diff --git a/common/networkstackclient/src/android/net/INetworkStackConnector.aidl b/common/networkstackclient/src/android/net/INetworkStackConnector.aidl
index 3751c36..fa7abf5 100644
--- a/common/networkstackclient/src/android/net/INetworkStackConnector.aidl
+++ b/common/networkstackclient/src/android/net/INetworkStackConnector.aidl
@@ -17,6 +17,7 @@
import android.net.IIpMemoryStoreCallbacks;
import android.net.INetworkMonitorCallbacks;
+import android.net.INetworkStackStatusCallback;
import android.net.Network;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
@@ -29,4 +30,21 @@
void makeNetworkMonitor(in Network network, String name, in INetworkMonitorCallbacks cb);
void makeIpClient(in String ifName, in IIpClientCallbacks callbacks);
void fetchIpMemoryStore(in IIpMemoryStoreCallbacks cb);
+ /**
+ * Mark a UID as test UID, allowing it to use the TestNetworkStackService.
+ *
+ * TestNetworkStackService is a binder service identical to NetworkStackService, but only
+ * available on userdebug builds, and only usable by the test UID. It does not require the
+ * MAINLINE_NETWORK_STACK signature permission like NetworkStackService does, so it allows the
+ * test UID to use other methods in this interface even though it would otherwise not have
+ * permission to.
+ *
+ * This method must be called as root and can only be used on debuggable builds. It only affects
+ * the NetworkStack until it is restarted.
+ * Callers should pass in -1 to reset after use.
+ *
+ * @param cb Callback that will be called with a status of 0 if the call succeeds. Calls without
+ * sufficient permissions may be dropped without generating a callback.
+ */
+ oneway void allowTestUid(int uid, in INetworkStackStatusCallback cb);
}
diff --git a/jarjar-rules-shared.txt b/jarjar-rules-shared.txt
index 048c976..e766b5f 100644
--- a/jarjar-rules-shared.txt
+++ b/jarjar-rules-shared.txt
@@ -12,3 +12,5 @@
rule android.net.DhcpResultsParcelable* @0
rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
rule android.util.LocalLog* android.net.networkstack.util.LocalLog@1
+
+rule android.util.IndentingPrintWriter* android.net.networkstack.util.AndroidUtilIndentingPrintWriter@1
diff --git a/jni/network_stack_utils_jni.cpp b/jni/network_stack_utils_jni.cpp
index f2ba575..22d573e 100644
--- a/jni/network_stack_utils_jni.cpp
+++ b/jni/network_stack_utils_jni.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "NetworkStackUtils-JNI"
+#include <dlfcn.h>
#include <errno.h>
#include <jni.h>
#include <linux/filter.h>
@@ -27,10 +28,12 @@
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <stdlib.h>
+#include <sys/system_properties.h>
#include <string>
#include <nativehelper/JNIHelp.h>
+
#include <android/log.h>
namespace android {
@@ -55,6 +58,76 @@
return true;
}
+static bool isAtLeastS() {
+ static bool atLeastS = false;
+ static bool doProbe = true;
+
+ if (!doProbe) {
+ return atLeastS;
+ }
+
+ atLeastS = (android_get_device_api_level() > __ANDROID_API_R__);
+ if (!atLeastS) {
+ // Check if this is a device with Android S dogfood build.
+ static constexpr const char* kCodenameProperty = "ro.build.version.codename";
+ char codename[PROP_VALUE_MAX] = { 0 };
+ // SDK may be 30 (R) with codename T if S SDK was finalized but not yet merged in the
+ // branch, and T development started.
+ atLeastS = (__system_property_get(kCodenameProperty, codename) > 0 &&
+ (strncmp(codename, "S", 2) == 0 || strncmp(codename, "T", 2) == 0));
+ }
+ doProbe = false;
+
+ return atLeastS;
+}
+
+static int getNativeFileDescriptorWithoutNdk(JNIEnv* env, jobject javaFd) {
+ // Prior to Android S, we need to find the descriptor field in the FileDescriptor class. The
+ // symbol name has been stable in libcore, but is a private implementation detail.
+ // Older libnativehelper_compat_c++ versions had a jniGetFdFromFileDescriptor method, but this
+ // was removed in S to replace it with the NDK API in libnativehelper.
+ // The code is copied here instead. This code can be removed once R is not supported anymore.
+ static jfieldID descriptorFieldID = nullptr;
+ if (descriptorFieldID == nullptr) {
+ jclass fileDescriptorClass = env->FindClass("java/io/FileDescriptor");
+ descriptorFieldID = env->GetFieldID(fileDescriptorClass, "descriptor", "I");
+ env->DeleteLocalRef(fileDescriptorClass);
+ if (descriptorFieldID == nullptr) {
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to get descriptor field.");
+ return -1;
+ }
+ }
+ return javaFd != nullptr ? env->GetIntField(javaFd, descriptorFieldID) : -1;
+}
+
+static int getNativeFileDescriptorWithNdk(JNIEnv* env, jobject javaFd) {
+ // Since Android S, there is an NDK API to get a file descriptor present in libnativehelper.so.
+ // libnativehelper is loaded into all processes by the zygote since the zygote uses it
+ // to load the Android Runtime and is also a public library (because of the NDK API).
+ static int (*ndkGetFD)(JNIEnv*, jobject) = nullptr;
+ if (ndkGetFD == nullptr) {
+ void* handle = dlopen("libnativehelper.so", RTLD_NOLOAD | RTLD_NODELETE);
+ ndkGetFD = reinterpret_cast<decltype(ndkGetFD)>(dlsym(handle, "AFileDescriptor_getFD"));
+ if (ndkGetFD == nullptr) {
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+ "Failed to dlsym(AFileDescriptor_getFD): %s", dlerror());
+ dlclose(handle);
+ return -1;
+ }
+ dlclose(handle);
+ }
+ return javaFd != nullptr ? ndkGetFD(env, javaFd) : -1;
+}
+
+static int getNativeFileDescriptor(JNIEnv* env, jobject javaFd) {
+ // Check if we should use the NDK File Descriptor API introduced in S.
+ if (isAtLeastS()) {
+ return getNativeFileDescriptorWithNdk(env, javaFd);
+ } else {
+ return getNativeFileDescriptorWithoutNdk(env, javaFd);
+ }
+}
+
static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
jbyteArray ipv4Addr, jstring ifname, jobject javaFd) {
arpreq req = {};
@@ -82,7 +155,7 @@
env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
req.arp_flags = ATF_COM; // Completed entry (ha valid)
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = getNativeFileDescriptor(env, javaFd);
if (fd < 0) {
jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
return;
@@ -120,7 +193,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = getNativeFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -153,7 +226,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = getNativeFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -228,7 +301,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = getNativeFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 10e7870..13700a6 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_name_connected" msgid="1795068343200033922">"Дамжих порталын нотолгоо"</string>
+ <string name="notification_channel_name_connected" msgid="1795068343200033922">"Дамжих порталын баталгаажуулалт"</string>
<string name="notification_channel_description_connected" msgid="7239184168268014518">"Төхөөрөмжийг дамжих порталын сүлжээнд амжилттай баталгаажуулсан үед харуулдаг мэдэгдлүүд"</string>
<string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"Сүлжээний байршлын мэдээлэл"</string>
<string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"Сүлжээнд байршлын мэдээллийн хуудас байгааг заах зорилгоор харуулдаг мэдэгдэл"</string>
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index 165e37b..173b5ef 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -778,7 +778,7 @@
mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
mLastSeen = currentTimeSeconds();
- // Sanity check packet in case a packet arrives before we attach RA filter
+ // Check packet in case a packet arrives before we attach RA filter
// to our packet socket. b/29586253
if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
diff --git a/src/android/net/dhcp/DhcpAckPacket.java b/src/android/net/dhcp/DhcpAckPacket.java
index 9e981ef..225e447 100644
--- a/src/android/net/dhcp/DhcpAckPacket.java
+++ b/src/android/net/dhcp/DhcpAckPacket.java
@@ -46,10 +46,11 @@
dnsServers += dnsServer.toString() + " ";
}
- return s + " ACK: your new IP " + mYourIp +
- ", netmask " + mSubnetMask +
- ", gateways " + mGateways + dnsServers +
- ", lease time " + mLeaseTime;
+ return s + " ACK: your new IP " + mYourIp
+ + ", netmask " + mSubnetMask
+ + ", gateways " + mGateways + dnsServers
+ + ", lease time " + mLeaseTime
+ + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : "");
}
/**
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index 4fedf30..f4deb47 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -20,6 +20,7 @@
import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL;
import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
+import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED;
import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
import static android.net.dhcp.DhcpPacket.DHCP_MTU;
import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
@@ -31,6 +32,7 @@
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
import static android.net.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION;
+import static android.net.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION;
import static android.net.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION;
import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
import static android.net.util.NetworkStackUtils.closeSocketQuietly;
@@ -104,6 +106,7 @@
import com.android.networkstack.arp.ArpPacket;
import com.android.networkstack.metrics.IpProvisioningMetrics;
+import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
@@ -244,6 +247,7 @@
/* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
+ public static final int DHCP_IPV6_ONLY = 3;
// Internal messages.
private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
@@ -287,13 +291,18 @@
@NonNull
private byte[] getRequestedParams() {
+ // Set an initial size large enough for all optional parameters that we might request.
+ final int numOptionalParams = 2;
+ final ByteArrayOutputStream params =
+ new ByteArrayOutputStream(DEFAULT_REQUESTED_PARAMS.length + numOptionalParams);
+ params.write(DEFAULT_REQUESTED_PARAMS, 0, DEFAULT_REQUESTED_PARAMS.length);
if (isCapportApiEnabled()) {
- final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS,
- DEFAULT_REQUESTED_PARAMS.length + 1);
- params[params.length - 1] = DHCP_CAPTIVE_PORTAL;
- return params;
+ params.write(DHCP_CAPTIVE_PORTAL);
}
- return DEFAULT_REQUESTED_PARAMS;
+ if (isIPv6OnlyPreferredModeEnabled()) {
+ params.write(DHCP_IPV6_ONLY_PREFERRED);
+ }
+ return params.toByteArray();
}
private static boolean isCapportApiEnabled() {
@@ -338,10 +347,10 @@
private int mConflictCount;
private long mLastAssignedIpv4AddressExpiry;
private Dependencies mDependencies;
- @NonNull
- private final NetworkStackIpMemoryStore mIpMemoryStore;
@Nullable
private DhcpPacketHandler mDhcpPacketHandler;
+ @NonNull
+ private final NetworkStackIpMemoryStore mIpMemoryStore;
@Nullable
private final String mHostname;
@@ -349,6 +358,10 @@
private long mLastInitEnterTime;
private long mLastBoundExitTime;
+ // 32-bit unsigned integer used to indicate the number of milliseconds the DHCP client should
+ // disable DHCPv4.
+ private long mIpv6OnlyWaitTimeMs;
+
// States.
private State mStoppedState = new StoppedState();
private State mDhcpState = new DhcpState();
@@ -370,6 +383,7 @@
new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);
private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState();
private State mDhcpDecliningState = new DhcpDecliningState();
+ private State mIpv6OnlyWaitState = new Ipv6OnlyWaitState();
private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
@@ -470,6 +484,7 @@
addState(mDhcpSelectingState, mDhcpState);
addState(mDhcpRequestingState, mDhcpState);
addState(mIpAddressConflictDetectingState, mDhcpState);
+ addState(mIpv6OnlyWaitState, mDhcpState);
addState(mDhcpHaveLeaseState, mDhcpState);
addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
addState(mDhcpBoundState, mDhcpHaveLeaseState);
@@ -544,6 +559,14 @@
false /* defaultEnabled */);
}
+ /**
+ * check whether or not to support IPv6-only preferred option.
+ */
+ public boolean isIPv6OnlyPreferredModeEnabled() {
+ return mDependencies.isFeatureEnabled(mContext, DHCP_IPV6_ONLY_PREFERRED_VERSION,
+ false /* defaultEnabled */);
+ }
+
private void recordMetricEnabledFeatures() {
if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT);
@@ -605,6 +628,13 @@
}
}
+ private byte[] getOptionsToSkip() {
+ final ByteArrayOutputStream optionsToSkip = new ByteArrayOutputStream(2);
+ if (!isCapportApiEnabled()) optionsToSkip.write(DHCP_CAPTIVE_PORTAL);
+ if (!isIPv6OnlyPreferredModeEnabled()) optionsToSkip.write(DHCP_IPV6_ONLY_PREFERRED);
+ return optionsToSkip.toByteArray();
+ }
+
private class DhcpPacketHandler extends PacketReader {
private FileDescriptor mPacketSock;
@@ -615,10 +645,8 @@
@Override
protected void handlePacket(byte[] recvbuf, int length) {
try {
- final byte[] optionsToSkip =
- isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL };
final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length,
- DhcpPacket.ENCAP_L2, optionsToSkip);
+ DhcpPacket.ENCAP_L2, getOptionsToSkip());
if (DBG) Log.d(TAG, "Received packet: " + packet);
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (DhcpPacket.ParseException e) {
@@ -944,10 +972,11 @@
// This is part of the initial configuration because it is passed in on startup and
// never updated.
// TODO: decide what to do about L2 key changes while the client is connected.
+ @Nullable
public final String l2Key;
public final boolean isPreconnectionEnabled;
- public Configuration(String l2Key, boolean isPreconnectionEnabled) {
+ public Configuration(@Nullable final String l2Key, final boolean isPreconnectionEnabled) {
this.l2Key = l2Key;
this.isPreconnectionEnabled = isPreconnectionEnabled;
}
@@ -1052,7 +1081,7 @@
}
abstract class TimeoutState extends LoggingState {
- protected int mTimeout = 0;
+ protected long mTimeout = 0;
@Override
public void enter() {
@@ -1223,6 +1252,15 @@
}
}
+ private boolean maybeTransitionToIpv6OnlyWaitState(@NonNull final DhcpPacket packet) {
+ if (!isIPv6OnlyPreferredModeEnabled()) return false;
+ if (packet.getIpv6OnlyWaitTimeMillis() == DhcpPacket.V6ONLY_PREFERRED_ABSENCE) return false;
+
+ mIpv6OnlyWaitTimeMs = packet.getIpv6OnlyWaitTimeMillis();
+ transitionTo(mIpv6OnlyWaitState);
+ return true;
+ }
+
private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) {
if (!isValidPacket(packet)) return;
@@ -1230,6 +1268,9 @@
// 2. received the DHCPACK packet from DHCP Servers that support Rapid
// Commit option, process it by following RFC4039.
if (packet instanceof DhcpOfferPacket) {
+ if (maybeTransitionToIpv6OnlyWaitState(packet)) {
+ return;
+ }
mOffer = packet.toDhcpResults();
if (mOffer != null) {
Log.d(TAG, "Got pending lease: " + mOffer);
@@ -1362,7 +1403,10 @@
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if ((packet instanceof DhcpAckPacket)) {
- DhcpResults results = packet.toDhcpResults();
+ if (maybeTransitionToIpv6OnlyWaitState(packet)) {
+ return;
+ }
+ final DhcpResults results = packet.toDhcpResults();
if (results != null) {
confirmDhcpLease(packet, results);
transitionTo(isDhcpIpConflictDetectEnabled()
@@ -1762,6 +1806,9 @@
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if ((packet instanceof DhcpAckPacket)) {
+ if (maybeTransitionToIpv6OnlyWaitState(packet)) {
+ return;
+ }
final DhcpResults results = packet.toDhcpResults();
if (results != null) {
if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
@@ -1905,6 +1952,32 @@
}
}
+ // This state is used for IPv6-only preferred mode defined in the draft-ietf-dhc-v6only.
+ // For IPv6-only capable host, it will forgo obtaining an IPv4 address for V6ONLY_WAIT
+ // period if the network indicates that it can provide IPv6 connectivity by replying
+ // with a valid IPv6-only preferred option in the DHCPOFFER or DHCPACK.
+ class Ipv6OnlyWaitState extends TimeoutState {
+ @Override
+ public void enter() {
+ mTimeout = mIpv6OnlyWaitTimeMs;
+ super.enter();
+
+ // Restore power save and suspend optimization if it was disabled before.
+ if (mRegisteredForPreDhcpNotification) {
+ mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_IPV6_ONLY, 0, null);
+ }
+ }
+
+ @Override
+ public void exit() {
+ mIpv6OnlyWaitTimeMs = 0;
+ }
+
+ protected void timeout() {
+ startInitRebootOrInit();
+ }
+ }
+
private void logState(String name, int durationMs) {
final DhcpClientEvent event = new DhcpClientEvent.Builder()
.setMsg(name)
diff --git a/src/android/net/dhcp/DhcpOfferPacket.java b/src/android/net/dhcp/DhcpOfferPacket.java
index aae08a7..e3e5d0f 100644
--- a/src/android/net/dhcp/DhcpOfferPacket.java
+++ b/src/android/net/dhcp/DhcpOfferPacket.java
@@ -47,9 +47,12 @@
}
}
- return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateways " + mGateways +
- " lease time " + mLeaseTime + ", domain " + mDomainName;
+ return s + " OFFER, ip " + mYourIp
+ + ", mask " + mSubnetMask + dnsServers
+ + ", gateways " + mGateways
+ + ", lease time " + mLeaseTime
+ + ", domain " + mDomainName
+ + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : "");
}
/**
diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java
index 3915740..b61146c 100644
--- a/src/android/net/dhcp/DhcpPacket.java
+++ b/src/android/net/dhcp/DhcpPacket.java
@@ -88,6 +88,10 @@
public static final int HWADDR_LEN = 16;
public static final int MAX_OPTION_LEN = 255;
+ // The lower boundary for V6ONLY_WAIT.
+ public static final long MIN_V6ONLY_WAIT_MS = 300_000;
+ public static final long V6ONLY_PREFERRED_ABSENCE = -1L;
+
/**
* The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
* IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
@@ -307,6 +311,16 @@
public static final byte DHCP_RAPID_COMMIT = 80;
protected boolean mRapidCommit;
+ /**
+ * DHCP IPv6-Only Preferred Option(draft-ietf-dhc-v6only).
+ * Indicate that a host supports an IPv6-only mode and willing to forgo obtaining an IPv4
+ * address for V6ONLY_WAIT period if the network provides IPv6 connectivity. V6ONLY_WAIT
+ * is 32-bit unsigned integer, so the Integer value cannot be used as-is.
+ */
+ public static final byte DHCP_IPV6_ONLY_PREFERRED = (byte) 108;
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ public Integer mIpv6OnlyWaitTime;
+
public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114;
protected String mCaptivePortalUrl;
@@ -789,6 +803,9 @@
if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
addTlv(buf, DHCP_MTU, mMtu);
}
+ if (mIpv6OnlyWaitTime != null) {
+ addTlv(buf, DHCP_IPV6_ONLY_PREFERRED, (int) Integer.toUnsignedLong(mIpv6OnlyWaitTime));
+ }
addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl);
}
@@ -942,6 +959,7 @@
Integer leaseTime = null;
Integer T1 = null;
Integer T2 = null;
+ Integer ipv6OnlyWaitTime = null;
// dhcp options
byte dhcpType = (byte) 0xFF;
@@ -1204,6 +1222,10 @@
expectedLen = optionLen;
captivePortalUrl = readAsciiString(packet, optionLen, true);
break;
+ case DHCP_IPV6_ONLY_PREFERRED:
+ expectedLen = 4;
+ ipv6OnlyWaitTime = Integer.valueOf(packet.getInt());
+ break;
default:
expectedLen = skipOption(packet, optionLen);
}
@@ -1292,6 +1314,7 @@
newPacket.mVendorId = vendorId;
newPacket.mVendorInfo = vendorInfo;
newPacket.mCaptivePortalUrl = captivePortalUrl;
+ newPacket.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
newPacket.mServerHostName = serverHostName;
} else {
@@ -1385,6 +1408,14 @@
}
/**
+ * Returns the IPv6-only wait time, in milliseconds, or -1 if the option is not present.
+ */
+ public long getIpv6OnlyWaitTimeMillis() {
+ if (mIpv6OnlyWaitTime == null) return V6ONLY_PREFERRED_ABSENCE;
+ return Math.max(MIN_V6ONLY_WAIT_MS, Integer.toUnsignedLong(mIpv6OnlyWaitTime) * 1000);
+ }
+
+ /**
* Builds a DHCP-DISCOVER packet from the required specified
* parameters.
*/
@@ -1399,15 +1430,14 @@
}
/**
- * Builds a DHCP-OFFER packet from the required specified
- * parameters.
+ * Builds a DHCP-OFFER packet from the required specified parameters.
*/
public static ByteBuffer buildOfferPacket(int encap, int transactionId,
boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu, String captivePortalUrl) {
+ short mtu, String captivePortalUrl, Integer ipv6OnlyWaitTime) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1424,10 +1454,27 @@
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
+ if (ipv6OnlyWaitTime != null) {
+ pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
+ }
return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
}
/**
+ * Builds a DHCP-OFFER packet from the required specified parameters.
+ */
+ public static ByteBuffer buildOfferPacket(int encap, int transactionId,
+ boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
+ Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
+ Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu, String captivePortalUrl) {
+ return buildOfferPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp,
+ mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier,
+ domainName, hostname, metered, mtu, captivePortalUrl, null /* V6ONLY_WAIT */);
+ }
+
+ /**
* Builds a DHCP-ACK packet from the required specified parameters.
*/
public static ByteBuffer buildAckPacket(int encap, int transactionId,
@@ -1435,7 +1482,7 @@
Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu, boolean rapidCommit, String captivePortalUrl) {
+ short mtu, boolean rapidCommit, String captivePortalUrl, Integer ipv6OnlyWaitTime) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
mac, rapidCommit);
@@ -1452,10 +1499,28 @@
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
+ if (ipv6OnlyWaitTime != null) {
+ pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
+ }
return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
}
/**
+ * Builds a DHCP-ACK packet from the required specified parameters.
+ */
+ public static ByteBuffer buildAckPacket(int encap, int transactionId,
+ boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
+ Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
+ Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
+ Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+ short mtu, boolean rapidCommit, String captivePortalUrl) {
+ return buildAckPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp,
+ requestClientIp, mac, timeout, netMask, bcAddr, gateways, dnsServers,
+ dhcpServerIdentifier, domainName, hostname, metered, mtu, rapidCommit,
+ captivePortalUrl, null /* V6ONLY_WAIT */);
+ }
+
+ /**
* Builds a DHCP-NAK packet from the required specified parameters.
*/
public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 6c95b5a..60c7323 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -99,6 +99,7 @@
private static final int CMD_UPDATE_PARAMS = 3;
@VisibleForTesting
protected static final int CMD_RECEIVE_PACKET = 4;
+ private static final int CMD_TERMINATE_AFTER_STOP = 5;
@NonNull
private final Context mContext;
@@ -362,10 +363,12 @@
* Stop listening for packets.
*
* <p>As the server is stopped asynchronously, some packets may still be processed shortly after
- * calling this method.
+ * calling this method. The server will also be cleaned up and can't be started again, even if
+ * it was already stopped.
*/
void stop(@Nullable INetworkStackStatusCallback cb) {
sendMessage(CMD_STOP_DHCP_SERVER, cb);
+ sendMessage(CMD_TERMINATE_AFTER_STOP);
}
private void maybeNotifyStatus(@Nullable INetworkStackStatusCallback cb, int statusCode) {
@@ -407,6 +410,9 @@
mEventCallbacks = obj.second;
transitionTo(mRunningState);
return HANDLED;
+ case CMD_TERMINATE_AFTER_STOP:
+ quit();
+ return HANDLED;
default:
return NOT_HANDLED;
}
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index c4f46ae..cc4a26c 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -777,6 +777,7 @@
private void stopStateMachineUpdaters() {
mObserverRegistry.unregisterObserver(mLinkObserver);
+ mLinkObserver.shutdown();
}
@Override
@@ -1582,7 +1583,11 @@
return;
}
- if (params.index != mInterfaceParams.index) {
+ // Check whether "mInterfaceParams" is null or not to prevent the potential NPE
+ // introduced if the interface was initially not found, but came back before this
+ // method was called. See b/162808916 for more details. TODO: query the new interface
+ // parameters by the interface index instead and check that the index has not changed.
+ if (mInterfaceParams == null || params.index != mInterfaceParams.index) {
Log.w(mTag, "interface: " + mInterfaceName + " has a different index: " + params.index);
return;
}
@@ -1601,8 +1606,16 @@
mL2Key = info.l2Key;
mCluster = info.cluster;
+ // Sometimes the wifi code passes in a null BSSID. Don't use Log.wtf in R because
+ // it's a known bug that will not be fixed in R.
if (info.bssid == null || mCurrentBssid == null) {
- Log.wtf(mTag, "bssid in the parcelable or current tracked bssid should be non-null");
+ final String msg = "bssid in the parcelable: " + info.bssid + " or "
+ + "current tracked bssid: " + mCurrentBssid + " is null";
+ if (ShimUtils.isAtLeastS()) {
+ Log.wtf(mTag, msg);
+ } else {
+ Log.w(mTag, msg);
+ }
return;
}
@@ -2156,7 +2169,8 @@
// a) initial address acquisition succeeds,
// b) renew succeeds or is NAK'd,
// c) rebind succeeds or is NAK'd, or
- // c) the lease expires,
+ // d) the lease expires, or
+ // e) the IPv6-only preferred option is enabled and entering Ipv6OnlyWaitState.
//
// but never when initial address acquisition fails. The latter
// condition is now governed by the provisioning timeout.
@@ -2170,6 +2184,8 @@
case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
break;
+ case DhcpClient.DHCP_IPV6_ONLY:
+ break;
default:
logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
}
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index 82f8d5d..5fd45a3 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -114,6 +114,7 @@
private DnsServerRepository mDnsServerRepository;
private final AlarmManager mAlarmManager;
private final Configuration mConfig;
+ private final Handler mHandler;
private final MyNetlinkMonitor mNetlinkMonitor;
@@ -127,11 +128,16 @@
mLinkProperties = new LinkProperties();
mLinkProperties.setInterfaceName(mInterfaceName);
mConfig = config;
+ mHandler = h;
mInterfaceLinkState = true; // Assume up by default
mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag);
- h.post(mNetlinkMonitor::start);
+ mHandler.post(mNetlinkMonitor::start);
+ }
+
+ public void shutdown() {
+ mHandler.post(mNetlinkMonitor::stop);
}
private void maybeLog(String operation, String iface, LinkAddress address) {
diff --git a/src/android/net/util/DataStallUtils.java b/src/android/net/util/DataStallUtils.java
index 2c5de57..4b25967 100644
--- a/src/android/net/util/DataStallUtils.java
+++ b/src/android/net/util/DataStallUtils.java
@@ -31,11 +31,12 @@
/** Detect data stall using tcp connection fail rate. */
public static final int DATA_STALL_EVALUATION_TYPE_TCP = 1 << 1;
- @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" }, value = {
- DATA_STALL_EVALUATION_TYPE_NONE,
- DATA_STALL_EVALUATION_TYPE_DNS,
- DATA_STALL_EVALUATION_TYPE_TCP,
- })
+ @IntDef(prefix = { "DATA_STALL_EVALUATION_TYPE_" },
+ flag = true,
+ value = {
+ DATA_STALL_EVALUATION_TYPE_NONE,
+ DATA_STALL_EVALUATION_TYPE_DNS,
+ DATA_STALL_EVALUATION_TYPE_TCP, })
@Retention(RetentionPolicy.SOURCE)
public @interface EvaluationType {
}
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java
index 94de7c3..de94ee3 100755
--- a/src/android/net/util/NetworkStackUtils.java
+++ b/src/android/net/util/NetworkStackUtils.java
@@ -18,10 +18,12 @@
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -210,6 +212,12 @@
public static final String DHCP_IP_CONFLICT_DETECT_VERSION = "dhcp_ip_conflict_detect_version";
/**
+ * Minimum module version at which to enable the IPv6-Only preferred option.
+ */
+ public static final String DHCP_IPV6_ONLY_PREFERRED_VERSION =
+ "dhcp_ipv6_only_preferred_version";
+
+ /**
* Minimum module version at which to enable dismissal CaptivePortalLogin app in validated
* network feature. CaptivePortalLogin app will also use validation facilities in
* {@link NetworkMonitor} to perform portal validation if feature is enabled.
@@ -469,4 +477,17 @@
}
return (int) value;
}
+
+ /**
+ * Gets boolean config from resources.
+ */
+ public static boolean getResBooleanConfig(@NonNull final Context context,
+ @BoolRes int configResource, final boolean defaultValue) {
+ final Resources res = context.getResources();
+ try {
+ return res.getBoolean(configResource);
+ } catch (Resources.NotFoundException e) {
+ return defaultValue;
+ }
+ }
}
diff --git a/src/com/android/networkstack/metrics/DataStallDetectionStats.java b/src/com/android/networkstack/metrics/DataStallDetectionStats.java
index 4de2ec0..131fb79 100644
--- a/src/com/android/networkstack/metrics/DataStallDetectionStats.java
+++ b/src/com/android/networkstack/metrics/DataStallDetectionStats.java
@@ -194,9 +194,9 @@
}
/**
- * Set the dns evaluation type into Builder.
+ * Set the data stall evaluation type into Builder.
*
- * @param type the return code of the dns event.
+ * @param type the signal type causing a data stall to be suspected.
* @return {@code this} {@link Builder} instance.
*/
public Builder setEvaluationType(int type) {
diff --git a/src/com/android/server/NetworkStackService.java b/src/com/android/server/NetworkStackService.java
index 8710e67..512f221 100644
--- a/src/com/android/server/NetworkStackService.java
+++ b/src/com/android/server/NetworkStackService.java
@@ -19,6 +19,7 @@
import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+import static android.net.util.NetworkStackUtils.getResBooleanConfig;
import static com.android.server.util.PermissionUtil.checkDumpPermission;
@@ -31,6 +32,7 @@
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkStackConnector;
+import android.net.INetworkStackStatusCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -58,6 +60,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.networkstack.NetworkStackNotifier;
+import com.android.networkstack.R;
import com.android.networkstack.apishim.common.ShimUtils;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
@@ -409,6 +412,15 @@
}
@Override
+ public void allowTestUid(int uid, @Nullable INetworkStackStatusCallback cb)
+ throws RemoteException {
+ // setTestUid does its own permission checks
+ PermissionUtil.setTestUid(mContext, uid);
+ mLog.i("Allowing test uid " + uid);
+ if (cb != null) cb.onStatusAvailable(0);
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
checkDumpPermission();
@@ -462,6 +474,11 @@
pw.decreaseIndent();
}
}
+
+ pw.println();
+ pw.print("useNeighborResource: ");
+ pw.println(getResBooleanConfig(mContext,
+ R.bool.config_no_sim_card_uses_neighbor_mcc, false));
}
/**
diff --git a/src/com/android/server/TestNetworkStackService.java b/src/com/android/server/TestNetworkStackService.java
new file mode 100644
index 0000000..23981e5
--- /dev/null
+++ b/src/com/android/server/TestNetworkStackService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.server;
+
+import static com.android.server.util.PermissionUtil.isDebuggableBuild;
+
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A {@link NetworkStackService} that can only be bound to on debuggable builds.
+ */
+public class TestNetworkStackService extends NetworkStackService {
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (!isDebuggableBuild()) {
+ throw new SecurityException(
+ "TestNetworkStackService is only available on debuggable builds");
+ }
+ return super.onBind(intent);
+ }
+}
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index 40de26e..9aff0ce 100755
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -77,6 +77,7 @@
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
import static android.net.util.NetworkStackUtils.TEST_URL_EXPIRATION_TIME;
+import static android.net.util.NetworkStackUtils.getResBooleanConfig;
import static android.net.util.NetworkStackUtils.isEmpty;
import static android.net.util.NetworkStackUtils.isIPv6ULA;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
@@ -150,7 +151,6 @@
import android.util.SparseArray;
import androidx.annotation.ArrayRes;
-import androidx.annotation.BoolRes;
import androidx.annotation.IntegerRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -415,6 +415,7 @@
private String mPrivateDnsProviderHostname = "";
private final Context mContext;
+ private final Context mCustomizedContext;
private final INetworkMonitorCallbacks mCallback;
private final int mCallbackVersion;
private final Network mCleartextDnsNetwork;
@@ -504,7 +505,8 @@
@Nullable
private final DnsStallDetector mDnsStallDetector;
private long mLastProbeTime;
- // The signal causing a data stall to be suspected. Reset to 0 after metrics are sent to statsd.
+ // A bitmask of signals causing a data stall to be suspected. Reset to
+ // {@link DataStallUtils#DATA_STALL_EVALUATION_TYPE_NONE} after metrics are sent to statsd.
private @EvaluationType int mDataStallTypeToCollect;
private boolean mAcceptPartialConnectivity = false;
private final EvaluationState mEvaluationState = new EvaluationState();
@@ -570,6 +572,7 @@
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mNotifier = serviceManager.getNotifier();
+ mCustomizedContext = getCustomizedContextOrDefault();
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
@@ -1533,7 +1536,7 @@
}
final int token = ++mProbeToken;
- final EvaluationThreadDeps deps = new EvaluationThreadDeps(mNetworkCapabilities);
+ final ValidationProperties deps = new ValidationProperties(mNetworkCapabilities);
mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
isCaptivePortal(deps))));
mThread.start();
@@ -1831,7 +1834,10 @@
}
try {
final List<CellInfo> cells = mTelephonyManager.getAllCellInfo();
- if (cells == null) return null;
+ if (cells == null) {
+ log("CellInfo is null");
+ return null;
+ }
final Map<String, Integer> countryCodeMap = new HashMap<>();
int maxCount = 0;
for (final CellInfo cell : cells) {
@@ -1874,6 +1880,7 @@
// Return customized context if carrier id can match a record in sCarrierIdToMccMnc.
final MccMncOverrideInfo overrideInfo = getMccMncOverrideInfo();
if (overrideInfo != null) {
+ log("Return customized context by MccMncOverrideInfo.");
return getContextByMccMnc(overrideInfo.mcc, overrideInfo.mnc);
}
@@ -1883,11 +1890,13 @@
getResBooleanConfig(mContext, R.bool.config_no_sim_card_uses_neighbor_mcc, false);
if (!useNeighborResource
|| TelephonyManager.SIM_STATE_READY == mTelephonyManager.getSimState()) {
+ if (useNeighborResource) log("Sim state is ready, return original context.");
return mContext;
}
final String mcc = getLocationMcc();
if (TextUtils.isEmpty(mcc)) {
+ log("Return original context due to getting mcc failed.");
return mContext;
}
@@ -1895,7 +1904,7 @@
}
@Nullable
- private String getTestUrl(@NonNull String key) {
+ private URL getTestUrl(@NonNull String key) {
final String strExpiration = mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
TEST_URL_EXPIRATION_TIME, null);
if (strExpiration == null) return null;
@@ -1911,17 +1920,17 @@
final long now = System.currentTimeMillis();
if (expTime < now || (expTime - now) > TEST_URL_EXPIRATION_MS) return null;
- return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
+ final String strUrl = mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
key, null /* defaultValue */);
+ if (!isValidTestUrl(strUrl)) return null;
+ return makeURL(strUrl);
}
private String getCaptivePortalServerHttpsUrl() {
- final String testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
- if (isValidTestUrl(testUrl)) return testUrl;
- final Context targetContext = getCustomizedContextOrDefault();
- return getSettingFromResource(targetContext,
+ return getSettingFromResource(mCustomizedContext,
R.string.config_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL,
- targetContext.getResources().getString(R.string.default_captive_portal_https_url));
+ mCustomizedContext.getResources().getString(
+ R.string.default_captive_portal_https_url));
}
private static boolean isValidTestUrl(@Nullable String url) {
@@ -1962,17 +1971,6 @@
}
}
- @VisibleForTesting
- boolean getResBooleanConfig(@NonNull final Context context,
- @BoolRes int configResource, final boolean defaultValue) {
- final Resources res = context.getResources();
- try {
- return res.getBoolean(configResource);
- } catch (Resources.NotFoundException e) {
- return defaultValue;
- }
- }
-
/**
* Gets integer config from resources.
*/
@@ -2009,12 +2007,10 @@
* on one URL that can be used, while NetworkMonitor may implement more complex logic.
*/
public String getCaptivePortalServerHttpUrl() {
- final String testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTP_URL);
- if (isValidTestUrl(testUrl)) return testUrl;
- final Context targetContext = getCustomizedContextOrDefault();
- return getSettingFromResource(targetContext,
+ return getSettingFromResource(mCustomizedContext,
R.string.config_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL,
- targetContext.getResources().getString(R.string.default_captive_portal_http_url));
+ mCustomizedContext.getResources().getString(
+ R.string.default_captive_portal_http_url));
}
private int getConsecutiveDnsTimeoutThreshold() {
@@ -2088,6 +2084,9 @@
}
private URL[] makeCaptivePortalHttpsUrls() {
+ final URL testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
+ if (testUrl != null) return new URL[] { testUrl };
+
final String firstUrl = getCaptivePortalServerHttpsUrl();
try {
final URL[] settingProviderUrls =
@@ -2106,6 +2105,9 @@
}
private URL[] makeCaptivePortalHttpUrls() {
+ final URL testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTP_URL);
+ if (testUrl != null) return new URL[] { testUrl };
+
final String firstUrl = getCaptivePortalServerHttpUrl();
try {
final URL[] settingProviderUrls =
@@ -2172,7 +2174,7 @@
*/
private <T> T[] getProbeUrlArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId,
@ArrayRes int defaultResId, @NonNull Function<String, T> resourceConverter) {
- final Resources res = getCustomizedContextOrDefault().getResources();
+ final Resources res = mCustomizedContext.getResources();
return getProbeUrlArrayConfig(providerValue, configResId, res.getStringArray(defaultResId),
resourceConverter);
}
@@ -2190,7 +2192,7 @@
*/
private <T> T[] getProbeUrlArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId,
String[] defaultConfig, @NonNull Function<String, T> resourceConverter) {
- final Resources res = getCustomizedContextOrDefault().getResources();
+ final Resources res = mCustomizedContext.getResources();
String[] configValue = res.getStringArray(configResId);
if (configValue.length == 0) {
@@ -2252,24 +2254,24 @@
}
/**
- * Parameters that can be accessed by the evaluation thread in a thread-safe way.
+ * Validation properties that can be accessed by the evaluation thread in a thread-safe way.
*
* Parameters such as LinkProperties and NetworkCapabilities cannot be accessed by the
* evaluation thread directly, as they are managed in the state machine thread and not
* synchronized. This class provides a copy of the required data that is not modified and can be
* used safely by the evaluation thread.
*/
- private static class EvaluationThreadDeps {
- // TODO: add parameters that are accessed in a non-thread-safe way from the evaluation
- // thread (read from LinkProperties, NetworkCapabilities, useHttps, validationStage)
+ private static class ValidationProperties {
+ // TODO: add other properties that are needed for evaluation and currently extracted in a
+ // non-thread-safe way from LinkProperties, NetworkCapabilities, etc.
private final boolean mIsTestNetwork;
- EvaluationThreadDeps(NetworkCapabilities nc) {
+ ValidationProperties(NetworkCapabilities nc) {
this.mIsTestNetwork = nc.hasTransport(TRANSPORT_TEST);
}
}
- private CaptivePortalProbeResult isCaptivePortal(EvaluationThreadDeps deps) {
+ private CaptivePortalProbeResult isCaptivePortal(ValidationProperties properties) {
if (!mIsCaptivePortalCheckEnabled) {
validationLog("Validation disabled.");
return CaptivePortalProbeResult.success(CaptivePortalProbeResult.PROBE_UNKNOWN);
@@ -2317,11 +2319,12 @@
reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result);
} else if (mUseHttps && httpsUrls.length == 1 && httpUrls.length == 1) {
// Probe results are reported inside sendHttpAndHttpsParallelWithFallbackProbes.
- result = sendHttpAndHttpsParallelWithFallbackProbes(deps, proxyInfo,
+ result = sendHttpAndHttpsParallelWithFallbackProbes(properties, proxyInfo,
httpsUrls[0], httpUrls[0]);
} else if (mUseHttps) {
// Support result aggregation from multiple Urls.
- result = sendMultiParallelHttpAndHttpsProbes(deps, proxyInfo, httpsUrls, httpUrls);
+ result = sendMultiParallelHttpAndHttpsProbes(properties, proxyInfo, httpsUrls,
+ httpUrls);
} else {
result = sendDnsAndHttpProbes(proxyInfo, httpUrls[0], ValidationProbeEvent.PROBE_HTTP);
reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result);
@@ -2613,12 +2616,12 @@
private final CountDownLatch mLatch;
private final Probe mProbe;
- ProbeThread(CountDownLatch latch, EvaluationThreadDeps deps, ProxyInfo proxy, URL url,
+ ProbeThread(CountDownLatch latch, ValidationProperties properties, ProxyInfo proxy, URL url,
int probeType, Uri captivePortalApiUrl) {
mLatch = latch;
mProbe = (probeType == ValidationProbeEvent.PROBE_HTTPS)
- ? new HttpsProbe(deps, proxy, url, captivePortalApiUrl)
- : new HttpProbe(deps, proxy, url, captivePortalApiUrl);
+ ? new HttpsProbe(properties, proxy, url, captivePortalApiUrl)
+ : new HttpProbe(properties, proxy, url, captivePortalApiUrl);
mResult = CaptivePortalProbeResult.failed(probeType);
}
@@ -2643,14 +2646,14 @@
}
private abstract static class Probe {
- protected final EvaluationThreadDeps mDeps;
+ protected final ValidationProperties mProperties;
protected final ProxyInfo mProxy;
protected final URL mUrl;
protected final Uri mCaptivePortalApiUrl;
- protected Probe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url,
+ protected Probe(ValidationProperties properties, ProxyInfo proxy, URL url,
Uri captivePortalApiUrl) {
- mDeps = deps;
+ mProperties = properties;
mProxy = proxy;
mUrl = url;
mCaptivePortalApiUrl = captivePortalApiUrl;
@@ -2660,8 +2663,9 @@
}
final class HttpsProbe extends Probe {
- HttpsProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) {
- super(deps, proxy, url, captivePortalApiUrl);
+ HttpsProbe(ValidationProperties properties, ProxyInfo proxy, URL url,
+ Uri captivePortalApiUrl) {
+ super(properties, proxy, url, captivePortalApiUrl);
}
@Override
@@ -2671,8 +2675,9 @@
}
final class HttpProbe extends Probe {
- HttpProbe(EvaluationThreadDeps deps, ProxyInfo proxy, URL url, Uri captivePortalApiUrl) {
- super(deps, proxy, url, captivePortalApiUrl);
+ HttpProbe(ValidationProperties properties, ProxyInfo proxy, URL url,
+ Uri captivePortalApiUrl) {
+ super(properties, proxy, url, captivePortalApiUrl);
}
private CaptivePortalDataShim sendCapportApiProbe() {
@@ -2686,7 +2691,7 @@
// Protocol must be HTTPS
// (as per https://www.ietf.org/id/draft-ietf-capport-api-07.txt, #4).
// Only allow HTTP on localhost, for testing.
- final boolean isTestLocalhostHttp = mDeps.mIsTestNetwork
+ final boolean isTestLocalhostHttp = mProperties.mIsTestNetwork
&& "localhost".equals(url.getHost()) && "http".equals(url.getProtocol());
if (!"https".equals(url.getProtocol()) && !isTestLocalhostHttp) {
validationLog("Invalid captive portal API protocol: " + url.getProtocol());
@@ -2784,8 +2789,8 @@
}
private CaptivePortalProbeResult sendMultiParallelHttpAndHttpsProbes(
- @NonNull EvaluationThreadDeps deps, @Nullable ProxyInfo proxy, @NonNull URL[] httpsUrls,
- @NonNull URL[] httpUrls) {
+ @NonNull ValidationProperties properties, @Nullable ProxyInfo proxy,
+ @NonNull URL[] httpsUrls, @NonNull URL[] httpUrls) {
// If multiple URLs are required to ensure the correctness of validation, send parallel
// probes to explore the result in separate probe threads and aggregate those results into
// one as the final result for either HTTP or HTTPS.
@@ -2810,12 +2815,12 @@
// TODO: Have the capport probe as a different probe for cleanliness.
final URL urlMaybeWithCapport = httpUrls[0];
for (final URL url : httpUrls) {
- futures.add(ecs.submit(() -> new HttpProbe(deps, proxy, url,
+ futures.add(ecs.submit(() -> new HttpProbe(properties, proxy, url,
url.equals(urlMaybeWithCapport) ? capportApiUrl : null).sendProbe()));
}
for (final URL url : httpsUrls) {
- futures.add(ecs.submit(() -> new HttpsProbe(deps, proxy, url, capportApiUrl)
+ futures.add(ecs.submit(() -> new HttpsProbe(properties, proxy, url, capportApiUrl)
.sendProbe()));
}
@@ -2912,15 +2917,15 @@
}
private CaptivePortalProbeResult sendHttpAndHttpsParallelWithFallbackProbes(
- EvaluationThreadDeps deps, ProxyInfo proxy, URL httpsUrl, URL httpUrl) {
+ ValidationProperties properties, ProxyInfo proxy, URL httpsUrl, URL httpUrl) {
// Number of probes to wait for. If a probe completes with a conclusive answer
// it shortcuts the latch immediately by forcing the count to 0.
final CountDownLatch latch = new CountDownLatch(2);
final Uri capportApiUrl = getCaptivePortalApiUrl(mLinkProperties);
- final ProbeThread httpsProbe = new ProbeThread(latch, deps, proxy, httpsUrl,
+ final ProbeThread httpsProbe = new ProbeThread(latch, properties, proxy, httpsUrl,
ValidationProbeEvent.PROBE_HTTPS, capportApiUrl);
- final ProbeThread httpProbe = new ProbeThread(latch, deps, proxy, httpUrl,
+ final ProbeThread httpProbe = new ProbeThread(latch, properties, proxy, httpUrl,
ValidationProbeEvent.PROBE_HTTP, capportApiUrl);
try {
@@ -3326,7 +3331,8 @@
return false;
}
- Boolean result = null;
+ int typeToCollect = 0;
+ final int notStall = -1;
final StringJoiner msg = (DBG || VDBG_STALL) ? new StringJoiner(", ") : null;
// Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
// possible traffic cost in metered network.
@@ -3341,18 +3347,9 @@
final TcpSocketTracker tst = getTcpSocketTracker();
if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_TCP) && tst != null) {
if (tst.getLatestReceivedCount() > 0) {
- result = false;
+ typeToCollect = notStall;
} else if (tst.isDataStallSuspected()) {
- result = true;
- mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_TCP;
-
- final DataStallReportParcelable p = new DataStallReportParcelable();
- p.detectionMethod = DETECTION_METHOD_TCP_METRICS;
- p.timestampMillis = SystemClock.elapsedRealtime();
- p.tcpPacketFailRate = tst.getLatestPacketFailPercentage();
- p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval();
-
- notifyDataStallSuspected(p);
+ typeToCollect |= DATA_STALL_EVALUATION_TYPE_TCP;
}
if (DBG || VDBG_STALL) {
msg.add("tcp packets received=" + tst.getLatestReceivedCount())
@@ -3364,32 +3361,48 @@
// 1. The number of consecutive DNS query timeouts >= mConsecutiveDnsTimeoutThreshold.
// 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
final DnsStallDetector dsd = getDnsStallDetector();
- if ((result == null) && (dsd != null)
+ if ((typeToCollect != notStall) && (dsd != null)
&& dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
- if (dsd.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
- mDataStallValidDnsTimeThreshold)) {
- result = true;
- mDataStallTypeToCollect = DATA_STALL_EVALUATION_TYPE_DNS;
+ if (dsd.isDataStallSuspected(
+ mConsecutiveDnsTimeoutThreshold, mDataStallValidDnsTimeThreshold)) {
+ typeToCollect |= DATA_STALL_EVALUATION_TYPE_DNS;
logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
-
- final DataStallReportParcelable p = new DataStallReportParcelable();
- p.detectionMethod = DETECTION_METHOD_DNS_EVENTS;
- p.timestampMillis = SystemClock.elapsedRealtime();
- p.dnsConsecutiveTimeouts = mDnsStallDetector.getConsecutiveTimeoutCount();
- notifyDataStallSuspected(p);
}
if (DBG || VDBG_STALL) {
msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount());
}
}
- // log only data stall suspected.
- if ((DBG && Boolean.TRUE.equals(result)) || VDBG_STALL) {
- log("isDataStall: result=" + result + ", " + msg);
+
+ if (typeToCollect > 0) {
+ mDataStallTypeToCollect = typeToCollect;
+ final DataStallReportParcelable p = new DataStallReportParcelable();
+ int detectionMethod = 0;
+ p.timestampMillis = SystemClock.elapsedRealtime();
+ if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_DNS)) {
+ detectionMethod |= DETECTION_METHOD_DNS_EVENTS;
+ p.dnsConsecutiveTimeouts = mDnsStallDetector.getConsecutiveTimeoutCount();
+ }
+
+ if (isDataStallTypeDetected(typeToCollect, DATA_STALL_EVALUATION_TYPE_TCP)) {
+ detectionMethod |= DETECTION_METHOD_TCP_METRICS;
+ p.tcpPacketFailRate = tst.getLatestPacketFailPercentage();
+ p.tcpMetricsCollectionPeriodMillis = getTcpPollingInterval();
+ }
+ p.detectionMethod = detectionMethod;
+ notifyDataStallSuspected(p);
}
- return (result == null) ? false : result;
+ // log only data stall suspected.
+ if ((DBG && (typeToCollect > 0)) || VDBG_STALL) {
+ log("isDataStall: result=" + typeToCollect + ", " + msg);
+ }
+
+ return typeToCollect > 0;
}
+ private static boolean isDataStallTypeDetected(int typeToCollect, int evaluationType) {
+ return (typeToCollect & evaluationType) != 0;
+ }
// Class to keep state of evaluation results and probe results.
//
// The main purpose was to ensure NetworkMonitor can notify ConnectivityService of probe results
diff --git a/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
index 70688ad..c8559c8 100644
--- a/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
+++ b/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
@@ -29,7 +29,7 @@
import android.os.RemoteException;
import android.util.Log;
-import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
/**
@@ -65,7 +65,8 @@
}
}
- private static final ArrayList<InterruptMaintenance> sInterruptList = new ArrayList<>();
+ private static final CopyOnWriteArrayList<InterruptMaintenance> sInterruptList =
+ new CopyOnWriteArrayList<>();
private static IpMemoryStoreService sIpMemoryStoreService;
@Override
diff --git a/src/com/android/server/util/PermissionUtil.java b/src/com/android/server/util/PermissionUtil.java
index 3dff715..fbd13d7 100644
--- a/src/com/android/server/util/PermissionUtil.java
+++ b/src/com/android/server/util/PermissionUtil.java
@@ -16,12 +16,18 @@
package com.android.server.util;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Binder.getCallingPid;
import static android.os.Binder.getCallingUid;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -30,6 +36,8 @@
public final class PermissionUtil {
private static final AtomicInteger sSystemPid = new AtomicInteger(-1);
+ private static volatile int sTestUid = Process.INVALID_UID;
+
/**
* Check that the caller is allowed to communicate with the network stack.
* @throws SecurityException The caller is not allowed to communicate with the network stack.
@@ -41,8 +49,9 @@
return;
}
- if (caller != Process.myUid() && // apps with NETWORK_STACK_UID
- UserHandle.getAppId(caller) != Process.BLUETOOTH_UID) {
+ if (caller != Process.myUid() // apps with NETWORK_STACK_UID
+ && UserHandle.getAppId(caller) != Process.BLUETOOTH_UID
+ && !isTestUid(caller)) {
throw new SecurityException("Invalid caller: " + caller);
}
}
@@ -67,6 +76,42 @@
}
}
+ private static boolean isTestUid(int uid) {
+ return uid == sTestUid;
+ }
+
+ /**
+ * Set a test uid that is allowed to call the NetworkStack. Pass in -1 to reset.
+ *
+ * <p>The UID must have a package with NETWORK_SETTINGS permissions when it is allowed.
+ */
+ public static void setTestUid(Context context, int uid) {
+ if (!isDebuggableBuild()) {
+ throw new SecurityException("Cannot set test UID on non-debuggable builds");
+ }
+ if (getCallingUid() != Process.ROOT_UID) {
+ throw new SecurityException("Only root can set the test UID");
+ }
+
+ if (uid == Process.INVALID_UID) {
+ sTestUid = uid;
+ return;
+ }
+
+ final PackageManager pm = context.getPackageManager();
+ final String[] packages = pm.getPackagesForUid(uid);
+ if (packages == null) {
+ throw new SecurityException("No package in uid " + uid);
+ }
+ final boolean hasPermission = Arrays.stream(packages).anyMatch(
+ p -> pm.checkPermission(NETWORK_SETTINGS, p) == PERMISSION_GRANTED);
+ if (!hasPermission) {
+ throw new SecurityException(
+ "The uid must have a package with NETWORK_SETTINGS permissions");
+ }
+ sTestUid = uid;
+ }
+
/**
* Check that the caller is allowed to dump the network stack, e.g. dumpsys.
* @throws SecurityException The caller is not allowed to dump the network stack.
@@ -79,6 +124,14 @@
}
}
+ /**
+ * @see android.os.Build.IS_DEBUGGABLE
+ */
+ public static boolean isDebuggableBuild() {
+ // TODO: consider adding Build.IS_DEBUGGABLE to @SystemApi
+ return SystemProperties.getInt("ro.debuggable", 0) == 1;
+ }
+
private PermissionUtil() {
throw new UnsupportedOperationException("This class is not to be instantiated");
}
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index af63f0e..833b7d9 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -30,7 +30,10 @@
java_defaults {
name: "NetworkStackIntegrationTestsDefaults",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
new file mode 100644
index 0000000..5a847a0
--- /dev/null
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
@@ -0,0 +1,38 @@
+/*
+ * 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 android.net.ip
+
+/**
+ * Tests for IpClient, run with signature permissions.
+ */
+class IpClientIntegrationTest : IpClientIntegrationTestCommon() {
+ override fun makeIIpClient(ifaceName: String, cb: IIpClientCallbacks): IIpClient {
+ return mIpc.makeConnector()
+ }
+
+ override fun useNetworkStackSignature() = true
+
+ override fun setDhcpFeatures(
+ isDhcpLeaseCacheEnabled: Boolean,
+ isRapidCommitEnabled: Boolean,
+ isDhcpIpConflictDetectEnabled: Boolean
+ ) {
+ mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled)
+ mDependencies.setDhcpRapidCommitEnabled(isRapidCommitEnabled)
+ mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled)
+ }
+}
\ No newline at end of file
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
similarity index 77%
rename from tests/integration/src/android/net/ip/IpClientIntegrationTest.java
rename to tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index 38eb84e..fda0aeb 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -19,11 +19,13 @@
import static android.net.dhcp.DhcpClient.EXPIRED_LEASE;
import static android.net.dhcp.DhcpPacket.DHCP_BOOTREQUEST;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED;
import static android.net.dhcp.DhcpPacket.DHCP_MAGIC_COOKIE;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_L2;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
import static android.net.ipmemorystore.Status.SUCCESS;
import static android.system.OsConstants.ETH_P_IPV6;
@@ -46,6 +48,7 @@
import static com.android.server.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN;
import static com.android.server.util.NetworkStackConstants.IPV6_LEN_OFFSET;
import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
@@ -66,10 +69,12 @@
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -110,6 +115,7 @@
import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
import android.net.ipmemorystore.Status;
import android.net.netlink.StructNdOptPref64;
+import android.net.networkstack.TestNetworkStackServiceClient;
import android.net.shared.Layer2Information;
import android.net.shared.ProvisioningConfiguration;
import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
@@ -120,6 +126,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -127,11 +134,13 @@
import android.system.ErrnoException;
import android.system.Os;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.ConstantsShim;
import com.android.networkstack.apishim.common.ShimUtils;
@@ -144,13 +153,14 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.HandlerUtils;
import com.android.testutils.TapPacketReader;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
@@ -158,8 +168,15 @@
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileReader;
import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
@@ -178,29 +195,61 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
+import kotlin.Lazy;
+import kotlin.LazyKt;
+
/**
- * Tests for IpClient.
+ * Base class for IpClient tests.
+ *
+ * Tests in this class can either be run with signature permissions, or with root access.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class IpClientIntegrationTest {
+public abstract class IpClientIntegrationTestCommon {
private static final int DATA_BUFFER_LEN = 4096;
private static final int PACKET_TIMEOUT_MS = 5_000;
- private static final int TEST_TIMEOUT_MS = 400;
private static final String TEST_L2KEY = "some l2key";
private static final String TEST_CLUSTER = "some cluster";
private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour
+ private static final int TEST_IPV6_ONLY_WAIT_S = 1_800; // 30 min
+ private static final int TEST_LOWER_IPV6_ONLY_WAIT_S = (int) (MIN_V6ONLY_WAIT_MS / 1000 - 1);
+ private static final int TEST_ZERO_IPV6_ONLY_WAIT_S = 0;
+ private static final long TEST_MAX_IPV6_ONLY_WAIT_S = 0xffffffffL;
// TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants.
private static final int IFA_F_STABLE_PRIVACY = 0x800;
+ protected static final long TEST_TIMEOUT_MS = 2_000L;
+
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+ @Rule
+ public final TestName mTestNameRule = new TestName();
+
+ /**
+ * Indicates that a test requires signature permissions to run.
+ *
+ * Such tests can only be run on devices that use known signing keys, so this annotation must be
+ * avoided as much as possible. Consider whether the test can be written to use shell and root
+ * shell permissions, and run against the NetworkStack AIDL interface (IIpClient) instead.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD})
+ private @interface SignatureRequiredTest {
+ String reason();
+ }
+
+ /**** BEGIN signature required test members ****/
+ // Do not use unless the test *really* cannot be written to exercise IIpClient without mocks.
+ // Tests using the below members must be annotated with @SignatureRequiredTest (otherwise the
+ // members will be null), and can only be run on devices that use known signing keys.
+ // The members could technically be moved to the IpClientIntegrationTest subclass together with
+ // the tests requiring signature permissions, but this would make it harder to follow tests in
+ // multiple classes, and harder to migrate tests between signature required and not required.
@Mock private Context mContext;
@Mock private ConnectivityManager mCm;
@Mock private Resources mResources;
- @Mock private IIpClientCallbacks mCb;
@Mock private AlarmManager mAlarm;
@Mock private ContentResolver mContentResolver;
@Mock private NetworkStackServiceManager mNetworkStackServiceManager;
@@ -209,17 +258,33 @@
@Mock private PowerManager.WakeLock mTimeoutWakeLock;
@Spy private INetd mNetd;
-
- private String mIfaceName;
private NetworkObserverRegistry mNetworkObserverRegistry;
+
+ protected IpClient mIpc;
+ protected Dependencies mDependencies;
+
+ /***** END signature required test members *****/
+
+ private IIpClientCallbacks mCb;
+ private IIpClient mIIpClient;
+ private String mIfaceName;
private HandlerThread mPacketReaderThread;
private Handler mHandler;
private TapPacketReader mPacketReader;
private FileDescriptor mTapFd;
- private IpClient mIpc;
- private Dependencies mDependencies;
private byte[] mClientMac;
+ private boolean mIsSignatureRequiredTest;
+
+ // ReadHeads for various packet streams. Cannot be initialized in @Before because ReadHead is
+ // single-thread-only, and AndroidJUnitRunner runs @Before and @Test on different threads.
+ // While it looks like these are created only once per test, they are actually created once per
+ // test method because JUnit recreates a fresh test class instance before every test method.
+ private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mDhcpPacketReadHead =
+ LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
+ private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mArpPacketReadHead =
+ LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
+
// Ethernet header
private static final int ETH_HEADER_LEN = 14;
@@ -272,7 +337,7 @@
private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster";
private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 };
- private class Dependencies extends IpClient.Dependencies {
+ protected class Dependencies extends IpClient.Dependencies {
private boolean mIsDhcpLeaseCacheEnabled;
private boolean mIsDhcpRapidCommitEnabled;
private boolean mIsDhcpIpConflictDetectEnabled;
@@ -281,6 +346,8 @@
private DhcpClient mDhcpClient;
private boolean mIsHostnameConfigurationEnabled;
private String mHostname;
+ private boolean mIsInterfaceRecovered;
+ private boolean mIsIPv6OnlyPreferredEnabled;
public void setDhcpLeaseCacheEnabled(final boolean enable) {
mIsDhcpLeaseCacheEnabled = enable;
@@ -294,11 +361,32 @@
mIsDhcpIpConflictDetectEnabled = enable;
}
+ public void setIPv6OnlyPreferredEnabled(final boolean enable) {
+ mIsIPv6OnlyPreferredEnabled = enable;
+ }
+
public void setHostnameConfiguration(final boolean enable, final String hostname) {
mIsHostnameConfigurationEnabled = enable;
mHostname = hostname;
}
+ // Enable this flag to simulate the interface has been added back after removing
+ // on the provisioning start. However, the actual tap interface has been removed,
+ // interface parameters query will get null when attempting to restore Interface
+ // MTU. Create a new InterfaceParams instance and return instead just for interface
+ // toggling test case.
+ public void simulateInterfaceRecover() {
+ mIsInterfaceRecovered = true;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifname) {
+ return mIsInterfaceRecovered
+ ? new InterfaceParams(ifname, 1 /* index */,
+ MacAddress.fromString("00:11:22:33:44:55"))
+ : super.getInterfaceParams(ifname);
+ }
+
@Override
public INetd getNetd(Context context) {
return mNetd;
@@ -331,6 +419,8 @@
return mIsDhcpLeaseCacheEnabled;
case NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION:
return mIsDhcpIpConflictDetectEnabled;
+ case NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION:
+ return mIsIPv6OnlyPreferredEnabled;
default:
fail("Invalid experiment flag: " + name);
return false;
@@ -374,8 +464,41 @@
}
}
+ @NonNull
+ protected abstract IIpClient makeIIpClient(
+ @NonNull String ifaceName, @NonNull IIpClientCallbacks cb);
+
+ protected abstract void setDhcpFeatures(boolean isDhcpLeaseCacheEnabled,
+ boolean isRapidCommitEnabled, boolean isDhcpIpConflictDetectEnabled);
+
+ protected abstract boolean useNetworkStackSignature();
+
+ protected final boolean testSkipped() {
+ // TODO: split out a test suite for root tests, and fail hard instead of skipping the test
+ // if it is run on devices where TestNetworkStackServiceClient is not supported
+ return !useNetworkStackSignature()
+ && (mIsSignatureRequiredTest || !TestNetworkStackServiceClient.isSupported());
+ }
+
@Before
public void setUp() throws Exception {
+ final Method testMethod = IpClientIntegrationTestCommon.class.getMethod(
+ mTestNameRule.getMethodName());
+ mIsSignatureRequiredTest = testMethod.getAnnotation(SignatureRequiredTest.class) != null;
+ assumeFalse(testSkipped());
+
+ setUpTapInterface();
+ mCb = mock(IIpClientCallbacks.class);
+
+ if (useNetworkStackSignature()) {
+ setUpMocks();
+ setUpIpClient();
+ }
+
+ mIIpClient = makeIIpClient(mIfaceName, mCb);
+ }
+
+ protected void setUpMocks() throws Exception {
MockitoAnnotations.initMocks(this);
mDependencies = new Dependencies();
@@ -393,9 +516,6 @@
mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
-
- setUpTapInterface();
- setUpIpClient();
}
private void awaitIpClientShutdown() throws Exception {
@@ -404,6 +524,7 @@
@After
public void tearDown() throws Exception {
+ if (testSkipped()) return;
if (mPacketReader != null) {
mHandler.post(() -> mPacketReader.stop()); // Also closes the socket
mTapFd = null;
@@ -411,11 +532,11 @@
if (mPacketReaderThread != null) {
mPacketReaderThread.quitSafely();
}
- mIpc.shutdown();
+ mIIpClient.shutdown();
awaitIpClientShutdown();
}
- private void setUpTapInterface() {
+ private void setUpTapInterface() throws Exception {
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
// Adopt the shell permission identity to create a test TAP interface.
inst.getUiAutomation().adoptShellPermissionIdentity();
@@ -431,8 +552,9 @@
inst.getUiAutomation().dropShellPermissionIdentity();
}
mIfaceName = iface.getInterfaceName();
- mClientMac = InterfaceParams.getByName(mIfaceName).macAddr.toByteArray();
- mPacketReaderThread = new HandlerThread(IpClientIntegrationTest.class.getSimpleName());
+ mClientMac = getIfaceMacAddr(mIfaceName).toByteArray();
+ mPacketReaderThread = new HandlerThread(
+ IpClientIntegrationTestCommon.class.getSimpleName());
mPacketReaderThread.start();
mHandler = mPacketReaderThread.getThreadHandler();
@@ -447,6 +569,32 @@
mHandler.post(() -> mPacketReader.start());
}
+ private MacAddress getIfaceMacAddr(String ifaceName) throws IOException {
+ // InterfaceParams.getByName requires CAP_NET_ADMIN: read the mac address with the shell
+ final String strMacAddr = getOneLineCommandOutput(
+ "su root cat /sys/class/net/" + ifaceName + "/address");
+ return MacAddress.fromString(strMacAddr);
+ }
+
+ private String getOneLineCommandOutput(String cmd) throws IOException {
+ try (ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommand(cmd);
+ BufferedReader reader = new BufferedReader(new FileReader(fd.getFileDescriptor()))) {
+ return reader.readLine();
+ }
+ }
+
+ private IpClient makeIpClient() throws Exception {
+ IpClient ipc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry,
+ mNetworkStackServiceManager, mDependencies);
+ // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests
+ // that mock IpClient's dependencies might interact with those mocks while IpClient is
+ // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted
+ // with while they are being stubbed.
+ HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
+ return ipc;
+ }
+
private void setUpIpClient() throws Exception {
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
final IBinder netdIBinder =
@@ -457,13 +605,7 @@
mNetworkObserverRegistry = new NetworkObserverRegistry();
mNetworkObserverRegistry.register(mNetd);
- mIpc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests
- // that mock IpClient's dependencies might interact with those mocks while IpClient is
- // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted
- // with while they are being stubbed.
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ mIpc = makeIpClient();
// Tell the IpMemoryStore immediately to answer any question about network attributes with a
// null response. Otherwise, the DHCP client will wait for two seconds before starting,
@@ -493,7 +635,8 @@
inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener));
}
- private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) {
+ private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, long afterSeconds,
+ Handler handler) {
// Allow +/- 3 seconds to prevent flaky tests.
final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000;
final long min = when - 3 * 1000;
@@ -501,10 +644,14 @@
ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class);
verifyWithTimeout(inOrder, mAlarm).setExact(
anyInt(), longThat(x -> x >= min && x <= max),
- contains(tagMatch), captor.capture(), eq(mIpc.getHandler()));
+ contains(tagMatch), captor.capture(), eq(handler));
return captor.getValue();
}
+ private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) {
+ return expectAlarmSet(inOrder, tagMatch, (long) afterSeconds, mIpc.getHandler());
+ }
+
private boolean packetContainsExpectedField(final byte[] packet, final int offset,
final byte[] expected) {
if (packet.length < offset + expected.length) return false;
@@ -548,20 +695,29 @@
}
private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
- final Integer leaseTimeSec, final short mtu, final String captivePortalUrl) {
+ final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
+ final String captivePortalUrl, final Integer ipv6OnlyWaitTime) {
return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
- CLIENT_ADDR /* yourIp */, packet.getClientMac(), leaseTimeSec,
+ clientAddress /* yourIp */, packet.getClientMac(), leaseTimeSec,
NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu, captivePortalUrl);
+ false /* metered */, mtu, captivePortalUrl, ipv6OnlyWaitTime);
+ }
+
+ private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
+ final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
+ final String captivePortalUrl) {
+ return buildDhcpOfferPacket(packet, clientAddress, leaseTimeSec, mtu, captivePortalUrl,
+ null /* ipv6OnlyWaitTime */);
}
private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
- final boolean rapidCommit, final String captivePortalApiUrl) {
+ final boolean rapidCommit, final String captivePortalApiUrl,
+ final Integer ipv6OnlyWaitTime) {
return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
@@ -569,7 +725,14 @@
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu, rapidCommit, captivePortalApiUrl);
+ false /* metered */, mtu, rapidCommit, captivePortalApiUrl, ipv6OnlyWaitTime);
+ }
+
+ private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
+ final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
+ final boolean rapidCommit, final String captivePortalApiUrl) {
+ return buildDhcpAckPacket(packet, clientAddress, leaseTimeSec, mtu, rapidCommit,
+ captivePortalApiUrl, null /* ipv6OnlyWaitTime */);
}
private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) {
@@ -594,12 +757,14 @@
mPacketReader.sendResponse(packet);
}
+ private void startIpClientProvisioning(final ProvisioningConfiguration cfg) throws Exception {
+ mIIpClient.startProvisioning(cfg.toStableParcelable());
+ }
+
private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled,
final boolean isDhcpIpConflictDetectEnabled,
- final boolean isHostnameConfigurationEnabled, final String hostname,
- final String displayName, final ScanResultInfo scanResultInfo)
- throws RemoteException {
+ final String displayName, final ScanResultInfo scanResultInfo) throws Exception {
ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
@@ -609,11 +774,10 @@
if (displayName != null) prov.withDisplayName(displayName);
if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo);
- mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled);
- mDependencies.setDhcpRapidCommitEnabled(shouldReplyRapidCommitAck);
- mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled);
- mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, hostname);
- mIpc.startProvisioning(prov.build());
+ setDhcpFeatures(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
+ isDhcpIpConflictDetectEnabled);
+
+ startIpClientProvisioning(prov.build());
if (!isPreconnectionEnabled) {
verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
}
@@ -622,11 +786,9 @@
private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled,
- final boolean isDhcpIpConflictDetectEnabled)
- throws RemoteException {
+ final boolean isDhcpIpConflictDetectEnabled) throws Exception {
startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled,
isPreconnectionEnabled, isDhcpIpConflictDetectEnabled,
- false /* isHostnameConfigurationEnabled */, null /* hostname */,
null /* displayName */, null /* ScanResultInfo */);
}
@@ -687,9 +849,10 @@
final boolean isHostnameConfigurationEnabled, final String hostname,
final String captivePortalApiUrl, final String displayName,
final ScanResultInfo scanResultInfo) throws Exception {
+ mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, hostname);
startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
- isHostnameConfigurationEnabled, hostname, displayName, scanResultInfo);
+ displayName, scanResultInfo);
return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
captivePortalApiUrl);
}
@@ -706,8 +869,8 @@
mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec,
(short) mtu, true /* rapidCommit */, captivePortalApiUrl));
} else {
- mPacketReader.sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec,
- (short) mtu, captivePortalApiUrl));
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
+ leaseTimeSec, (short) mtu, captivePortalApiUrl));
}
} else if (packet instanceof DhcpRequestPacket) {
final ByteBuffer byteBuffer = isSuccessLease
@@ -745,13 +908,9 @@
}
private DhcpPacket getNextDhcpPacket() throws ParseException {
- byte[] packet;
- while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) {
- if (!isDhcpPacket(packet)) continue;
- return DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L2);
- }
- fail("No expected DHCP packet received on interface within timeout");
- return null;
+ byte[] packet = mDhcpPacketReadHead.getValue().poll(PACKET_TIMEOUT_MS, this::isDhcpPacket);
+ assertNotNull("No expected DHCP packet received on interface within timeout", packet);
+ return DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L2);
}
private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout)
@@ -873,7 +1032,7 @@
}
mIpc.notifyPreconnectionComplete(false /* abort */);
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
@@ -896,8 +1055,8 @@
final short mtu = (short) TEST_DEFAULT_MTU;
if (!shouldReplyRapidCommitAck) {
- mPacketReader.sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
- null /* captivePortalUrl */));
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
+ TEST_LEASE_DURATION_S, mtu, null /* captivePortalUrl */));
packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpRequestPacket);
}
@@ -906,7 +1065,7 @@
if (!shouldAbortPreconnection) {
mIpc.notifyPreconnectionComplete(true /* success */);
- HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
// If timeout fires after successful preconnection, right now DhcpClient will have
// already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So
@@ -926,9 +1085,9 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- private ArpPacket getNextArpPacket(final int timeout) throws Exception {
+ private ArpPacket getNextArpPacket(final long timeout) throws Exception {
byte[] packet;
- while ((packet = mPacketReader.popPacket(timeout)) != null) {
+ while ((packet = mArpPacketReadHead.getValue().poll(timeout, p -> true)) != null) {
final ArpPacket arpPacket = parseArpPacketOrNull(packet);
if (arpPacket != null) return arpPacket;
}
@@ -1003,7 +1162,7 @@
}
}
- @Test
+ @Test @SignatureRequiredTest(reason = "InterfaceParams.getByName requires CAP_NET_ADMIN")
public void testInterfaceParams() throws Exception {
InterfaceParams params = InterfaceParams.getByName(mIfaceName);
assertNotNull(params);
@@ -1012,7 +1171,7 @@
assertNotNull(params.macAddr);
assertTrue(params.hasMacAddress);
- // Sanity check.
+ // Check interface "lo".
params = InterfaceParams.getByName("lo");
assertNotNull(params);
assertEquals("lo", params.name);
@@ -1030,7 +1189,7 @@
assertTrue(packet instanceof DhcpDiscoverPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleSuccessDhcpLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
@@ -1040,7 +1199,7 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleFailureDhcpLease() throws Exception {
performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S,
true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
@@ -1050,7 +1209,7 @@
assertIpMemoryNeverStoreNetworkAttributes();
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleInfiniteLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE,
@@ -1060,7 +1219,7 @@
assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleNoLease() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */,
@@ -1071,6 +1230,7 @@
}
@Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R.
+ @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleDisableInitRebootState() throws Exception {
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
@@ -1079,7 +1239,7 @@
assertIpMemoryNeverStoreNetworkAttributes();
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHandleRapidCommitOption() throws Exception {
final long currentTime = System.currentTimeMillis();
performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
@@ -1089,7 +1249,7 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientStartWithCachedInfiniteLease() throws Exception {
final DhcpPacket packet = getReplyFromDhcpLease(
new NetworkAttributes.Builder()
@@ -1102,7 +1262,7 @@
assertTrue(packet instanceof DhcpRequestPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientStartWithCachedExpiredLease() throws Exception {
final DhcpPacket packet = getReplyFromDhcpLease(
new NetworkAttributes.Builder()
@@ -1115,13 +1275,13 @@
assertTrue(packet instanceof DhcpDiscoverPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception {
final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */);
assertTrue(packet instanceof DhcpDiscoverPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception {
final DhcpPacket packet = getReplyFromDhcpLease(
new NetworkAttributes.Builder()
@@ -1134,7 +1294,7 @@
assertTrue(packet instanceof DhcpDiscoverPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception {
final DhcpPacket packet = getReplyFromDhcpLease(
new NetworkAttributes.Builder()
@@ -1145,7 +1305,7 @@
assertTrue(packet instanceof DhcpDiscoverPacket);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientRapidCommitEnabled() throws Exception {
startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
@@ -1155,6 +1315,7 @@
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpServerInLinkProperties() throws Exception {
assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
@@ -1164,17 +1325,17 @@
assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress());
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu() throws Exception {
doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception {
doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu_WithException() throws Exception {
doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd)
.interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
@@ -1183,12 +1344,12 @@
assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception {
doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
throws Exception {
removeTapInterface(mTapFd);
@@ -1202,7 +1363,7 @@
verify(mCb, never()).setNeighborDiscoveryOffload(true);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception {
long currentTime = System.currentTimeMillis();
@@ -1231,6 +1392,36 @@
assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU);
}
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRestoreInitialInterfaceMtu_removeInterfaceAndAddback() throws Exception {
+ doAnswer(invocation -> {
+ final LinkProperties lp = invocation.getArgument(0);
+ assertEquals(lp.getInterfaceName(), mIfaceName);
+ assertEquals(0, lp.getLinkAddresses().size());
+ assertEquals(0, lp.getDnsServers().size());
+
+ mDependencies.simulateInterfaceRecover();
+ return null;
+ }).when(mCb).onProvisioningFailure(any());
+
+ final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIpReachabilityMonitor()
+ .withoutIPv6()
+ .build();
+
+ // Intend to remove the tap interface and force IpClient throw provisioning failure
+ // due to that interface is not found.
+ removeTapInterface(mTapFd);
+ assertNull(InterfaceParams.getByName(mIfaceName));
+
+ mIpc.startProvisioning(config);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
+
+ // Make sure everything queued by this test was processed (e.g. transition to StoppingState
+ // from ClearingIpAddressState) and tearDown will check if IpClient exits normally or crash.
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ }
+
private boolean isRouterSolicitation(final byte[] packetBytes) {
ByteBuffer packet = ByteBuffer.wrap(packetBytes);
return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6
@@ -1240,11 +1431,8 @@
}
private void waitForRouterSolicitation() throws ParseException {
- byte[] packet;
- while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) {
- if (isRouterSolicitation(packet)) return;
- }
- fail("No router solicitation received on interface within timeout");
+ assertNotNull("No router solicitation received on interface within timeout",
+ mPacketReader.popPacket(PACKET_TIMEOUT_MS, this::isRouterSolicitation));
}
private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception {
@@ -1446,7 +1634,7 @@
return lp;
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testRaRdnss() throws Exception {
ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
@@ -1509,6 +1697,7 @@
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testPref64Option() throws Exception {
assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
@@ -1614,14 +1803,14 @@
// Check that the alarm is cancelled when IpClient is stopped.
mIpc.stop();
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
expectAlarmCancelled(inOrder, clearAlarm);
expectNat64PrefixUpdate(inOrder, null);
// Check that even if the alarm was already in the message queue while it was cancelled, it
// is safely ignored.
mIpc.getHandler().post(() -> clearAlarm.onAlarm());
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
}
private void addIpAddressAndWaitForIt(final String iface) throws Exception {
@@ -1656,7 +1845,7 @@
}
// Wait for IpClient to process the addition of the address.
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
}
private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception {
@@ -1686,7 +1875,7 @@
addIpAddressAndWaitForIt(mIfaceName);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testIpClientClearingIpAddressState() throws Exception {
doIPv4OnlyProvisioningAndExitWithLeftAddress();
@@ -1707,7 +1896,7 @@
assertEquals("0.0.0.0", cfg.ipv4Addr);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception {
doIPv4OnlyProvisioningAndExitWithLeftAddress();
@@ -1720,45 +1909,45 @@
// Force to enter RunningState.
mIpc.notifyPreconnectionComplete(false /* abort */);
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_success() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_Abort() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
true /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit()
throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
@@ -1766,28 +1955,28 @@
true /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
true /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit()
throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
@@ -1795,14 +1984,14 @@
true /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception {
doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit()
throws Exception {
doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
@@ -1810,7 +1999,7 @@
false /* timeoutBeforePreconnectionComplete */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception {
// For FILS connection, current bssid (also l2key and cluster) is still null when
// starting provisioning since the L2 link hasn't been established yet. Ensure that
@@ -1828,67 +2017,67 @@
// Force IpClient transition to RunningState from PreconnectionState.
mIpc.notifyPreconnectionComplete(false /* success */);
- HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_conflictByArpReply() throws Exception {
doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
true /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_conflictByArpProbe() throws Exception {
doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception {
doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_WithoutIpConflict() throws Exception {
doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception {
doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception {
doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
true /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception {
doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception {
doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
false /* shouldResponseArpReply */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHostname_enableConfig() throws Exception {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
@@ -1903,7 +2092,7 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHostname_disableConfig() throws Exception {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
@@ -1918,7 +2107,7 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testHostname_enableConfigWithNullHostname() throws Exception {
final long currentTime = System.currentTimeMillis();
final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
@@ -1945,42 +2134,43 @@
// Send Offer and handle Request -> Ack
final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
- mPacketReader.sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S,
- (short) TEST_DEFAULT_MTU, serverSentUrl));
+ mPacketReader.sendResponse(buildDhcpOfferPacket(discover, CLIENT_ADDR,
+ TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, serverSentUrl));
final int testMtu = 1345;
handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl);
final Uri expectedUrl = featureEnabled && serverSendsOption
? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
- // Wait for LinkProperties containing DHCP-obtained info, such as MTU
+ // LinkProperties will be updated multiple times. Wait for it to contain DHCP-obtained info,
+ // such as MTU.
final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
- verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
+ verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
argThat(lp -> lp.getMtu() == testMtu));
// Ensure that the URL was set as expected in the callbacks.
// Can't verify the URL up to Q as there is no such attribute in LinkProperties.
if (!ShimUtils.isAtLeastR()) return;
- verify(mCb).onLinkPropertiesChange(captor.capture());
+ verify(mCb, atLeastOnce()).onLinkPropertiesChange(captor.capture());
assertTrue(captor.getAllValues().stream().anyMatch(
lp -> Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl())));
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
// Only run the test on platforms / builds where the API is enabled
assumeTrue(CaptivePortalDataShimImpl.isSupported());
runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
// Only run the test on platforms / builds where the API is enabled
assumeTrue(CaptivePortalDataShimImpl.isSupported());
runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
// Only run the test on platforms / builds where the API is disabled
assumeFalse(CaptivePortalDataShimImpl.isSupported());
@@ -2041,7 +2231,7 @@
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection() throws Exception {
byte[] data = new byte[10];
new Random().nextBytes(data);
@@ -2050,7 +2240,7 @@
true /* expectMetered */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection_incorrectIeId() throws Exception {
byte[] data = new byte[10];
new Random().nextBytes(data);
@@ -2059,7 +2249,7 @@
false /* expectMetered */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection_incorrectOUI() throws Exception {
byte[] data = new byte[10];
new Random().nextBytes(data);
@@ -2068,7 +2258,7 @@
false /* expectMetered */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection_incorrectSsid() throws Exception {
byte[] data = new byte[10];
new Random().nextBytes(data);
@@ -2077,7 +2267,7 @@
false /* expectMetered */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection_incorrectType() throws Exception {
byte[] data = new byte[10];
new Random().nextBytes(data);
@@ -2086,7 +2276,7 @@
false /* expectMetered */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testUpstreamHotspotDetection_zeroLengthData() throws Exception {
byte[] data = new byte[0];
doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
@@ -2097,7 +2287,8 @@
private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName,
final String ssid, final String bssid, final boolean expectRoaming) throws Exception {
long currentTime = System.currentTimeMillis();
- final ScanResultInfo scanResultInfo = makeScanResultInfo(ssid, bssid);
+ final ScanResultInfo scanResultInfo = (ssid == null || bssid == null)
+ ? null : makeScanResultInfo(ssid, bssid);
doAnswer(invocation -> {
// we don't rely on the Init-Reboot state to renew previous cached IP lease.
@@ -2141,7 +2332,7 @@
mPacketReader.sendResponse(buildDhcpAckPacket(packet,
hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR, TEST_LEASE_DURATION_S,
(short) TEST_DEFAULT_MTU, false /* rapidcommit */, null /* captivePortalUrl */));
- HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
if (hasMismatchedIpAddress) {
// notifyFailure
ArgumentCaptor<DhcpResultsParcelable> captor =
@@ -2159,31 +2350,37 @@
}
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpRoaming() throws Exception {
doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpRoaming_invalidBssid() throws Exception {
doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
TEST_DHCP_ROAM_SSID, TEST_DHCP_ROAM_BSSID, false /* expectRoaming */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDhcpRoaming_nullScanResultInfo() throws Exception {
+ doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
+ null /* SSID */, null /* BSSID */, false /* expectRoaming */);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpRoaming_invalidSsid() throws Exception {
doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpRoaming_invalidDisplayName() throws Exception {
doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */,
TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception {
doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */);
@@ -2225,7 +2422,7 @@
reset(mCb);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testIgnoreIpv6ProvisioningLoss() throws Exception {
doDualStackProvisioning();
@@ -2251,10 +2448,210 @@
assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
}
- @Test
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDualStackProvisioning() throws Exception {
doDualStackProvisioning();
verify(mCb, never()).onProvisioningFailure(any());
}
+
+ private DhcpPacket verifyDhcpPacketRequestsIPv6OnlyPreferredOption(
+ Class<? extends DhcpPacket> packetType) throws Exception {
+ final DhcpPacket packet = getNextDhcpPacket();
+ assertTrue(packetType.isInstance(packet));
+ assertTrue(packet.hasRequestedParam(DHCP_IPV6_ONLY_PREFERRED));
+ return packet;
+ }
+
+ private void doIPv6OnlyPreferredOptionTest(final Integer ipv6OnlyWaitTime,
+ final Inet4Address clientAddress) throws Exception {
+ final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIpReachabilityMonitor()
+ .build();
+ mDependencies.setIPv6OnlyPreferredEnabled(true);
+ mIpc.startProvisioning(config);
+
+ final DhcpPacket packet =
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
+
+ // Respond DHCPOFFER with IPv6-Only preferred option and offered address.
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, clientAddress,
+ TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */,
+ ipv6OnlyWaitTime));
+ }
+
+ private void doDiscoverIPv6OnlyPreferredOptionTest(final int optionSecs,
+ final long expectedWaitSecs) throws Exception {
+ doIPv6OnlyPreferredOptionTest(optionSecs, CLIENT_ADDR);
+ final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT",
+ expectedWaitSecs, mDependencies.mDhcpClient.getHandler());
+ mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
+ // Implicitly check that the client never sent a DHCPREQUEST to request the offered address.
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption() throws Exception {
+ doDiscoverIPv6OnlyPreferredOptionTest(TEST_IPV6_ONLY_WAIT_S, TEST_IPV6_ONLY_WAIT_S);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
+ doDiscoverIPv6OnlyPreferredOptionTest(TEST_LOWER_IPV6_ONLY_WAIT_S,
+ TEST_LOWER_IPV6_ONLY_WAIT_S);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
+ doDiscoverIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S,
+ TEST_LOWER_IPV6_ONLY_WAIT_S);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
+ doDiscoverIPv6OnlyPreferredOptionTest((int) TEST_MAX_IPV6_ONLY_WAIT_S, 0xffffffffL);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWaitWithOfferedAnyAddress()
+ throws Exception {
+ doIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S, IPV4_ADDR_ANY);
+
+ final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 300,
+ mDependencies.mDhcpClient.getHandler());
+ mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
+
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_enabledPreconnection() throws Exception {
+ final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIpReachabilityMonitor()
+ .withPreconnection()
+ .build();
+
+ mDependencies.setDhcpRapidCommitEnabled(true);
+ mDependencies.setIPv6OnlyPreferredEnabled(true);
+ mIpc.startProvisioning(config);
+
+ final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
+ verify(mCb).setNeighborDiscoveryOffload(true);
+
+ // Force IpClient transition to RunningState from PreconnectionState.
+ mIpc.notifyPreconnectionComplete(true /* success */);
+ HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
+
+ // DHCP server SHOULD NOT honor the Rapid-Commit option if the response would
+ // contain the IPv6-only Preferred option to the client, instead respond with
+ // a DHCPOFFER.
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
+ (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */, TEST_IPV6_ONLY_WAIT_S));
+
+ final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 1800,
+ mDependencies.mDhcpClient.getHandler());
+ mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
+
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testDiscoverIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
+ doIPv6OnlyPreferredOptionTest(null /* ipv6OnlyWaitTime */, CLIENT_ADDR);
+
+ // The IPv6-only Preferred option SHOULD be included in the Parameter Request List option
+ // in DHCPREQUEST messages after receiving a DHCPOFFER without this option.
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
+ }
+
+ private void startFromInitRebootStateWithIPv6OnlyPreferredOption(final Integer ipv6OnlyWaitTime,
+ final long expectedWaitSecs) throws Exception {
+ doAnswer(invocation -> {
+ ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
+ .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY,
+ new NetworkAttributes.Builder()
+ .setAssignedV4Address(CLIENT_ADDR)
+ .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
+ .setMtu(new Integer(TEST_DEFAULT_MTU))
+ .setCluster(TEST_CLUSTER)
+ .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
+ .build());
+ return null;
+ }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
+
+ final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIpReachabilityMonitor()
+ .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
+ MacAddress.fromString(TEST_DEFAULT_BSSID)))
+ .build();
+
+ mDependencies.setDhcpLeaseCacheEnabled(true);
+ mDependencies.setIPv6OnlyPreferredEnabled(true);
+ mIpc.startProvisioning(config);
+
+ final DhcpPacket packet =
+ verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
+
+ // Respond DHCPACK with IPv6-Only preferred option.
+ mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR,
+ TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, false /* rapidcommit */,
+ null /* captivePortalUrl */, ipv6OnlyWaitTime));
+
+ if (ipv6OnlyWaitTime != null) {
+ expectAlarmSet(null /* inOrder */, "TIMEOUT", expectedWaitSecs,
+ mDependencies.mDhcpClient.getHandler());
+ }
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRequestIPv6OnlyPreferredOption() throws Exception {
+ startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_IPV6_ONLY_WAIT_S,
+ TEST_IPV6_ONLY_WAIT_S);
+
+ // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
+ // IPv6-Only preferred option(default value) in the DHCPACK packet.
+ assertIpMemoryNeverStoreNetworkAttributes();
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRequestIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
+ startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_LOWER_IPV6_ONLY_WAIT_S,
+ TEST_LOWER_IPV6_ONLY_WAIT_S);
+
+ // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
+ // IPv6-Only preferred option(less than MIN_V6ONLY_WAIT_MS) in the DHCPACK packet.
+ assertIpMemoryNeverStoreNetworkAttributes();
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRequestIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
+ startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_ZERO_IPV6_ONLY_WAIT_S,
+ TEST_LOWER_IPV6_ONLY_WAIT_S);
+
+ // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
+ // IPv6-Only preferred option(0) in the DHCPACK packet.
+ assertIpMemoryNeverStoreNetworkAttributes();
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRequestIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
+ startFromInitRebootStateWithIPv6OnlyPreferredOption((int) TEST_MAX_IPV6_ONLY_WAIT_S,
+ 0xffffffffL);
+
+ // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
+ // IPv6-Only preferred option(MAX_UNSIGNED_INTEGER: 0xFFFFFFFF) in the DHCPACK packet.
+ assertIpMemoryNeverStoreNetworkAttributes();
+ }
+
+ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
+ public void testRequestIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
+ final long currentTime = System.currentTimeMillis();
+ startFromInitRebootStateWithIPv6OnlyPreferredOption(null /* ipv6OnlyWaitTime */,
+ 0 /* expectedWaitSecs */);
+
+ // Client processes DHCPACK packet normally and transits to the ConfiguringInterfaceState
+ // due to the null V6ONLY_WAIT.
+ assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
+ }
}
diff --git a/tests/integration/src/android/net/ip/IpClientRootTest.kt b/tests/integration/src/android/net/ip/IpClientRootTest.kt
new file mode 100644
index 0000000..77b612d
--- /dev/null
+++ b/tests/integration/src/android/net/ip/IpClientRootTest.kt
@@ -0,0 +1,203 @@
+/*
+ * 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 android.net.ip
+
+import android.Manifest
+import android.Manifest.permission.READ_DEVICE_CONFIG
+import android.Manifest.permission.WRITE_DEVICE_CONFIG
+import android.net.IIpMemoryStore
+import android.net.IIpMemoryStoreCallbacks
+import android.net.networkstack.TestNetworkStackServiceClient
+import android.net.util.NetworkStackUtils
+import android.os.Process
+import android.provider.DeviceConfig
+import android.util.ArrayMap
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.After
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+import java.lang.System.currentTimeMillis
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+// Stable AIDL method 5 in INetworkStackConnector is allowTestUid
+private const val ALLOW_TEST_UID_INDEX = 5
+
+/**
+ * Tests for IpClient, run with root access but no signature permissions.
+ *
+ * Tests run from this class interact with the real network stack process and can affect system
+ * state, e.g. by changing flags.
+ * State should be restored at the end of the test, but is not guaranteed if the test process is
+ * terminated during the run.
+ */
+class IpClientRootTest : IpClientIntegrationTestCommon() {
+ companion object {
+ private val TAG = IpClientRootTest::class.java.simpleName
+ private val automation by lazy { InstrumentationRegistry.getInstrumentation().uiAutomation }
+ private lateinit var nsClient: TestNetworkStackServiceClient
+
+ private class IpMemoryStoreCallbacks(
+ private val fetchedFuture: CompletableFuture<IIpMemoryStore>
+ ) : IIpMemoryStoreCallbacks.Stub() {
+ override fun onIpMemoryStoreFetched(ipMemoryStore: IIpMemoryStore) {
+ fetchedFuture.complete(ipMemoryStore)
+ }
+ override fun getInterfaceVersion() = IIpMemoryStoreCallbacks.VERSION
+ override fun getInterfaceHash() = IIpMemoryStoreCallbacks.HASH
+ }
+
+ @JvmStatic @BeforeClass
+ fun setUpClass() {
+ // Connect to the NetworkStack only once, as it is relatively expensive (~50ms plus any
+ // polling time waiting for the test UID to be allowed), and there should be minimal
+ // side-effects between tests compared to reconnecting every time.
+ automation.adoptShellPermissionIdentity(Manifest.permission.NETWORK_SETTINGS)
+ try {
+ automation.executeShellCommand("su root service call network_stack " +
+ "$ALLOW_TEST_UID_INDEX i32 " + Process.myUid())
+ // Connecting to the test service does not require allowing the test UID (binding is
+ // always allowed with NETWORK_SETTINGS permissions on debuggable builds), but
+ // allowing the test UID is required to call any method on the service.
+ nsClient = TestNetworkStackServiceClient.connect()
+ // Wait for oneway call to be processed: unfortunately there is no easy way to wait
+ // for a success callback via the service shell command.
+ // TODO: build a small native util that also waits for the success callback, bundle
+ // it in the test APK, and run it as shell command as root instead.
+ waitUntilTestUidAllowed()
+ } finally {
+ automation.dropShellPermissionIdentity()
+ }
+ }
+
+ private fun waitUntilTestUidAllowed() {
+ // Until the test UID is allowed, oneway binder calls will not receive any reply.
+ // Call fetchIpMemoryStore (which has limited side-effects) repeatedly until any call
+ // gets a callback.
+ val limit = currentTimeMillis() + TEST_TIMEOUT_MS
+ val fetchedFuture = CompletableFuture<IIpMemoryStore>()
+ Log.i(TAG, "Starting multiple attempts to fetch IpMemoryStore; failures are expected")
+ while (currentTimeMillis() < limit) {
+ try {
+ nsClient.fetchIpMemoryStore(IpMemoryStoreCallbacks(fetchedFuture))
+ // The future may be completed by any previous call to fetchIpMemoryStore.
+ fetchedFuture.get(20, TimeUnit.MILLISECONDS)
+ Log.i(TAG, "Obtained IpMemoryStore")
+ return
+ } catch (e: TimeoutException) {
+ // Fall through
+ }
+ }
+ }
+
+ @JvmStatic @AfterClass
+ fun tearDownClass() {
+ nsClient.disconnect()
+ automation.adoptShellPermissionIdentity(Manifest.permission.NETWORK_SETTINGS)
+ try {
+ // Reset the test UID as -1.
+ // This may not be called if the test process is terminated before completing,
+ // however this is fine as the test UID is only usable on userdebug builds, and
+ // the system does not reuse UIDs across apps until reboot.
+ automation.executeShellCommand("su root service call network_stack " +
+ "$ALLOW_TEST_UID_INDEX i32 -1")
+ } finally {
+ automation.dropShellPermissionIdentity()
+ }
+ }
+ }
+
+ private val originalFlagValues = ArrayMap<String, String>()
+
+ /**
+ * Wrapper class for IIpClientCallbacks.
+ *
+ * Used to delegate method calls to mock interfaces used to verify the calls, while using
+ * real implementations of the binder stub (such as [asBinder]) to properly receive the calls.
+ */
+ private class BinderCbWrapper(base: IIpClientCallbacks) :
+ IIpClientCallbacks.Stub(), IIpClientCallbacks by base {
+ // asBinder is implemented by both base class and delegate: specify explicitly
+ override fun asBinder() = super.asBinder()
+ }
+
+ @After
+ fun tearDownFlags() {
+ if (testSkipped()) return
+ automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
+ try {
+ for ((key, value) in originalFlagValues.entries) {
+ if (key == null) continue
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, key,
+ value, false /* makeDefault */)
+ }
+ } finally {
+ automation.dropShellPermissionIdentity()
+ }
+ }
+
+ override fun useNetworkStackSignature() = false
+
+ override fun makeIIpClient(ifaceName: String, cbMock: IIpClientCallbacks): IIpClient {
+ val ipClientCaptor = ArgumentCaptor.forClass(IIpClient::class.java)
+ // Older versions of NetworkStack do not clear the calling identity when creating IpClient,
+ // so READ_DEVICE_CONFIG is required to initialize it (b/168577898).
+ automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG)
+ try {
+ nsClient.makeIpClient(ifaceName, BinderCbWrapper(cbMock))
+ verify(cbMock, timeout(TEST_TIMEOUT_MS)).onIpClientCreated(ipClientCaptor.capture())
+ } finally {
+ automation.dropShellPermissionIdentity()
+ }
+ return ipClientCaptor.value
+ }
+
+ override fun setDhcpFeatures(
+ isDhcpLeaseCacheEnabled: Boolean,
+ isRapidCommitEnabled: Boolean,
+ isDhcpIpConflictDetectEnabled: Boolean
+ ) {
+ automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
+ try {
+ setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled)
+ setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled)
+ setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION,
+ isDhcpIpConflictDetectEnabled)
+ } finally {
+ automation.dropShellPermissionIdentity()
+ }
+ }
+
+ private fun setFeatureEnabled(feature: String, enabled: Boolean) {
+ // Do not use computeIfAbsent as it would overwrite null values (flag originally unset)
+ if (!originalFlagValues.containsKey(feature)) {
+ originalFlagValues[feature] =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature)
+ }
+ // The feature is enabled if the flag is lower than the package version.
+ // Package versions follow a standard format with 9 digits.
+ // TODO: consider resetting flag values on reboot when set to special values like "1" or
+ // "999999999"
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature,
+ if (enabled) "1" else "999999999", false)
+ }
+}
\ No newline at end of file
diff --git a/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt b/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt
new file mode 100644
index 0000000..bc3e5e0
--- /dev/null
+++ b/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt
@@ -0,0 +1,81 @@
+/*
+ * 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 android.net.networkstack
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
+import android.net.INetworkStackConnector
+import android.os.IBinder
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.test.fail
+
+/**
+ * A [NetworkStackClientBase] that binds to [com.android.server.TestNetworkStackService]
+ */
+class TestNetworkStackServiceClient private constructor() : NetworkStackClientBase() {
+ companion object {
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val networkStackVersion by lazy {
+ val component = getNetworkStackComponent()
+ val info = context.packageManager.getPackageInfo(component.packageName, 0 /* flags */)
+ info.longVersionCode
+ }
+
+ /**
+ * Create a [TestNetworkStackServiceClient] and connect it to the NetworkStack.
+ */
+ @JvmStatic
+ fun connect(): TestNetworkStackServiceClient {
+ return TestNetworkStackServiceClient().apply { init() }
+ }
+
+ @JvmStatic
+ fun isSupported(): Boolean {
+ // TestNetworkStackService was introduced in NetworkStack version 301100000.
+ // It is also available at HEAD in development branches, where the version code is
+ // 300000000.
+ return networkStackVersion == 300000000L || networkStackVersion >= 301100000L
+ }
+
+ private fun getNetworkStackComponent(): ComponentName {
+ val connectorIntent = Intent(INetworkStackConnector::class.java.name)
+ return connectorIntent.resolveSystemService(context.packageManager, MATCH_SYSTEM_ONLY)
+ ?: fail("TestNetworkStackService not found")
+ }
+ }
+
+ private val serviceConnection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
+ onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(service))
+ }
+
+ override fun onServiceDisconnected(name: ComponentName) = Unit
+ }
+
+ private fun init() {
+ val bindIntent = Intent(INetworkStackConnector::class.java.name + ".Test")
+ bindIntent.component = getNetworkStackComponent()
+ context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE)
+ }
+
+ fun disconnect() {
+ InstrumentationRegistry.getInstrumentation().context.unbindService(serviceConnection)
+ }
+}
\ No newline at end of file
diff --git a/tests/lib/Android.bp b/tests/lib/Android.bp
deleted file mode 100644
index 47b950d..0000000
--- a/tests/lib/Android.bp
+++ /dev/null
@@ -1,56 +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.
-//
-
-java_library {
- name: "net-tests-utils-multivariant",
- srcs: [
- "multivariant/**/*.java",
- "multivariant/**/*.kt",
- ],
- host_supported: true,
- static_libs: [
- "kotlin-test",
- "junit",
- ],
-}
-
-java_library {
- name: "net-tests-utils",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ":net-module-utils-srcs-for-tests",
- ],
- defaults: ["lib_mockito_extended"],
- libs: [
- "androidx.annotation_annotation",
- ],
- static_libs: [
- "androidx.test.ext.junit",
- "net-tests-utils-multivariant",
- ],
-}
-
-java_defaults {
- name: "lib_mockito_extended",
- static_libs: [
- "mockito-target-extended-minus-junit4"
- ],
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
-}
diff --git a/tests/lib/multivariant/com/android/testutils/ConcurrentUtils.kt b/tests/lib/multivariant/com/android/testutils/ConcurrentUtils.kt
deleted file mode 100644
index a365af5..0000000
--- a/tests/lib/multivariant/com/android/testutils/ConcurrentUtils.kt
+++ /dev/null
@@ -1,26 +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.testutils
-
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import kotlin.system.measureTimeMillis
-
-// For Java usage
-fun durationOf(fn: Runnable) = measureTimeMillis { fn.run() }
-
-fun CountDownLatch.await(timeoutMs: Long): Boolean = await(timeoutMs, TimeUnit.MILLISECONDS)
diff --git a/tests/lib/multivariant/com/android/testutils/ExceptionUtils.java b/tests/lib/multivariant/com/android/testutils/ExceptionUtils.java
deleted file mode 100644
index e7dbed5..0000000
--- a/tests/lib/multivariant/com/android/testutils/ExceptionUtils.java
+++ /dev/null
@@ -1,67 +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.testutils;
-
-import java.util.function.Supplier;
-
-public class ExceptionUtils {
- /**
- * Like a Consumer, but declared to throw an exception.
- * @param <T>
- */
- @FunctionalInterface
- public interface ThrowingConsumer<T> {
- void accept(T t) throws Exception;
- }
-
- /**
- * Like a Supplier, but declared to throw an exception.
- * @param <T>
- */
- @FunctionalInterface
- public interface ThrowingSupplier<T> {
- T get() throws Exception;
- }
-
- /**
- * Like a Runnable, but declared to throw an exception.
- */
- @FunctionalInterface
- public interface ThrowingRunnable {
- void run() throws Exception;
- }
-
-
- public static <T> Supplier<T> ignoreExceptions(ThrowingSupplier<T> func) {
- return () -> {
- try {
- return func.get();
- } catch (Exception e) {
- return null;
- }
- };
- }
-
- public static Runnable ignoreExceptions(ThrowingRunnable r) {
- return () -> {
- try {
- r.run();
- } catch (Exception e) {
- }
- };
- }
-}
diff --git a/tests/lib/multivariant/com/android/testutils/FileUtils.kt b/tests/lib/multivariant/com/android/testutils/FileUtils.kt
deleted file mode 100644
index 678f977..0000000
--- a/tests/lib/multivariant/com/android/testutils/FileUtils.kt
+++ /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.testutils
-
-// This function is private because the 2 is hardcoded here, and is not correct if not called
-// directly from __LINE__ or __FILE__.
-private fun callerStackTrace(): StackTraceElement = try {
- throw RuntimeException()
-} catch (e: RuntimeException) {
- e.stackTrace[2] // 0 is here, 1 is get() in __FILE__ or __LINE__
-}
-val __FILE__: String get() = callerStackTrace().fileName
-val __LINE__: Int get() = callerStackTrace().lineNumber
diff --git a/tests/lib/multivariant/com/android/testutils/MiscAsserts.kt b/tests/lib/multivariant/com/android/testutils/MiscAsserts.kt
deleted file mode 100644
index 32c22c2..0000000
--- a/tests/lib/multivariant/com/android/testutils/MiscAsserts.kt
+++ /dev/null
@@ -1,103 +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.testutils
-
-import com.android.testutils.ExceptionUtils.ThrowingRunnable
-import java.lang.reflect.Modifier
-import kotlin.system.measureTimeMillis
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-private const val TAG = "Connectivity unit test"
-
-fun <T> assertEmpty(ts: Array<T>) = ts.size.let { len ->
- assertEquals(0, len, "Expected empty array, but length was $len")
-}
-
-fun <T> assertLength(expected: Int, got: Array<T>) = got.size.let { len ->
- assertEquals(expected, len, "Expected array of length $expected, but was $len for $got")
-}
-
-// Bridge method to help write this in Java. If you're writing Kotlin, consider using native
-// kotlin.test.assertFailsWith instead, as that method is reified and inlined.
-fun <T : Exception> assertThrows(expected: Class<T>, block: ThrowingRunnable): T {
- return assertFailsWith(expected.kotlin) { block.run() }
-}
-
-fun <T : Exception> assertThrows(msg: String, expected: Class<T>, block: ThrowingRunnable): T {
- return assertFailsWith(expected.kotlin, msg) { block.run() }
-}
-
-fun <T> assertEqualBothWays(o1: T, o2: T) {
- assertTrue(o1 == o2)
- assertTrue(o2 == o1)
-}
-
-fun <T> assertNotEqualEitherWay(o1: T, o2: T) {
- assertFalse(o1 == o2)
- assertFalse(o2 == o1)
-}
-
-fun assertStringContains(got: String, want: String) {
- assertTrue(got.contains(want), "$got did not contain \"${want}\"")
-}
-
-fun assertContainsExactly(actual: IntArray, vararg expected: Int) {
- // IntArray#sorted() returns a list, so it's fine to test with equals()
- assertEquals(actual.sorted(), expected.sorted(),
- "$actual does not contain exactly $expected")
-}
-
-fun assertContainsStringsExactly(actual: Array<String>, vararg expected: String) {
- assertEquals(actual.sorted(), expected.sorted(),
- "$actual does not contain exactly $expected")
-}
-
-fun <T> assertContainsAll(list: Collection<T>, vararg elems: T) {
- assertContainsAll(list, elems.asList())
-}
-
-fun <T> assertContainsAll(list: Collection<T>, elems: Collection<T>) {
- elems.forEach { assertTrue(list.contains(it), "$it not in list") }
-}
-
-fun assertRunsInAtMost(descr: String, timeLimit: Long, fn: Runnable) {
- assertRunsInAtMost(descr, timeLimit) { fn.run() }
-}
-
-fun assertRunsInAtMost(descr: String, timeLimit: Long, fn: () -> Unit) {
- val timeTaken = measureTimeMillis(fn)
- val msg = String.format("%s: took %dms, limit was %dms", descr, timeTaken, timeLimit)
- assertTrue(timeTaken <= timeLimit, msg)
-}
-
-/**
- * Verifies that the number of nonstatic fields in a java class equals a given count.
- * Note: this is essentially not useful for Kotlin code where fields are not really a thing.
- *
- * This assertion serves as a reminder to update test code around it if fields are added
- * after the test is written.
- * @param count Expected number of nonstatic fields in the class.
- * @param clazz Class to test.
- */
-fun <T> assertFieldCountEquals(count: Int, clazz: Class<T>) {
- assertEquals(count, clazz.declaredFields.filter {
- !Modifier.isStatic(it.modifiers) && !Modifier.isTransient(it.modifiers)
- }.size)
-}
diff --git a/tests/lib/multivariant/com/android/testutils/PacketFilter.kt b/tests/lib/multivariant/com/android/testutils/PacketFilter.kt
deleted file mode 100644
index cd8d6a5..0000000
--- a/tests/lib/multivariant/com/android/testutils/PacketFilter.kt
+++ /dev/null
@@ -1,87 +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.testutils
-
-import java.util.function.Predicate
-
-const val ETHER_TYPE_OFFSET = 12
-const val ETHER_HEADER_LENGTH = 14
-const val IPV4_PROTOCOL_OFFSET = ETHER_HEADER_LENGTH + 9
-const val IPV4_CHECKSUM_OFFSET = ETHER_HEADER_LENGTH + 10
-const val IPV4_HEADER_LENGTH = 20
-const val IPV4_UDP_OFFSET = ETHER_HEADER_LENGTH + IPV4_HEADER_LENGTH
-const val BOOTP_OFFSET = IPV4_UDP_OFFSET + 8
-const val BOOTP_TID_OFFSET = BOOTP_OFFSET + 4
-const val BOOTP_CLIENT_MAC_OFFSET = BOOTP_OFFSET + 28
-const val DHCP_OPTIONS_OFFSET = BOOTP_OFFSET + 240
-
-/**
- * A [Predicate] that matches a [ByteArray] if it contains the specified [bytes] at the specified
- * [offset].
- */
-class OffsetFilter(val offset: Int, vararg val bytes: Byte) : Predicate<ByteArray> {
- override fun test(packet: ByteArray) =
- bytes.withIndex().all { it.value == packet[offset + it.index] }
-}
-
-/**
- * A [Predicate] that matches ethernet-encapped packets that contain an UDP over IPv4 datagram.
- */
-class IPv4UdpFilter : Predicate<ByteArray> {
- private val impl = OffsetFilter(ETHER_TYPE_OFFSET, 0x08, 0x00 /* IPv4 */).and(
- OffsetFilter(IPV4_PROTOCOL_OFFSET, 17 /* UDP */))
- override fun test(t: ByteArray) = impl.test(t)
-}
-
-/**
- * A [Predicate] that matches ethernet-encapped DHCP packets sent from a DHCP client.
- */
-class DhcpClientPacketFilter : Predicate<ByteArray> {
- private val impl = IPv4UdpFilter()
- .and(OffsetFilter(IPV4_UDP_OFFSET /* source port */, 0x00, 0x44 /* 68 */))
- .and(OffsetFilter(IPV4_UDP_OFFSET + 2 /* dest port */, 0x00, 0x43 /* 67 */))
- override fun test(t: ByteArray) = impl.test(t)
-}
-
-/**
- * A [Predicate] that matches a [ByteArray] if it contains a ethernet-encapped DHCP packet that
- * contains the specified option with the specified [bytes] as value.
- */
-class DhcpOptionFilter(val option: Byte, vararg val bytes: Byte) : Predicate<ByteArray> {
- override fun test(packet: ByteArray): Boolean {
- val option = findDhcpOption(packet, option) ?: return false
- return option.contentEquals(bytes)
- }
-}
-
-/**
- * Find a DHCP option in a packet and return its value, if found.
- */
-fun findDhcpOption(packet: ByteArray, option: Byte): ByteArray? =
- findOptionOffset(packet, option, DHCP_OPTIONS_OFFSET)?.let {
- val optionLen = packet[it + 1]
- return packet.copyOfRange(it + 2 /* type, length bytes */, it + 2 + optionLen)
- }
-
-private tailrec fun findOptionOffset(packet: ByteArray, option: Byte, searchOffset: Int): Int? {
- if (packet.size <= searchOffset + 2 /* type, length bytes */) return null
-
- return if (packet[searchOffset] == option) searchOffset else {
- val optionLen = packet[searchOffset + 1]
- findOptionOffset(packet, option, searchOffset + 2 + optionLen)
- }
-}
diff --git a/tests/lib/multivariant/com/android/testutils/SkipPresubmit.kt b/tests/lib/multivariant/com/android/testutils/SkipPresubmit.kt
deleted file mode 100644
index 69ed048..0000000
--- a/tests/lib/multivariant/com/android/testutils/SkipPresubmit.kt
+++ /dev/null
@@ -1,24 +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.testutils
-
-/**
- * Skip the test in presubmit runs for the reason specified in [reason].
- *
- * This annotation is typically used to document hardware or test bench limitations.
- */
-annotation class SkipPresubmit(val reason: String)
\ No newline at end of file
diff --git a/tests/lib/multivariant/com/android/testutils/TrackRecord.kt b/tests/lib/multivariant/com/android/testutils/TrackRecord.kt
deleted file mode 100644
index 3cdea12..0000000
--- a/tests/lib/multivariant/com/android/testutils/TrackRecord.kt
+++ /dev/null
@@ -1,247 +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.testutils
-
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.locks.Condition
-import java.util.concurrent.locks.ReentrantLock
-import kotlin.concurrent.withLock
-
-/**
- * A List that additionally offers the ability to append via the add() method, and to retrieve
- * an element by its index optionally waiting for it to become available.
- */
-interface TrackRecord<E> : List<E> {
- /**
- * Adds an element to this queue, waking up threads waiting for one. Returns true, as
- * per the contract for List.
- */
- fun add(e: E): Boolean
-
- /**
- * Returns the first element after {@param pos}, possibly blocking until one is available, or
- * null if no such element can be found within the timeout.
- * If a predicate is given, only elements matching the predicate are returned.
- *
- * @param timeoutMs how long, in milliseconds, to wait at most (best effort approximation).
- * @param pos the position at which to start polling.
- * @param predicate an optional predicate to filter elements to be returned.
- * @return an element matching the predicate, or null if timeout.
- */
- fun poll(timeoutMs: Long, pos: Int, predicate: (E) -> Boolean = { true }): E?
-}
-
-/**
- * A thread-safe implementation of TrackRecord that is backed by an ArrayList.
- *
- * This class also supports the creation of a read-head for easier single-thread access.
- * Refer to the documentation of {@link ArrayTrackRecord.ReadHead}.
- */
-class ArrayTrackRecord<E> : TrackRecord<E> {
- private val lock = ReentrantLock()
- private val condition = lock.newCondition()
- // Backing store. This stores the elements in this ArrayTrackRecord.
- private val elements = ArrayList<E>()
-
- // The list iterator for RecordingQueue iterates over a snapshot of the collection at the
- // time the operator is created. Because TrackRecord is only ever mutated by appending,
- // that makes this iterator thread-safe as it sees an effectively immutable List.
- class ArrayTrackRecordIterator<E>(
- private val list: ArrayList<E>,
- start: Int,
- private val end: Int
- ) : ListIterator<E> {
- var index = start
- override fun hasNext() = index < end
- override fun next() = list[index++]
- override fun hasPrevious() = index > 0
- override fun nextIndex() = index + 1
- override fun previous() = list[--index]
- override fun previousIndex() = index - 1
- }
-
- // List<E> implementation
- override val size get() = lock.withLock { elements.size }
- override fun contains(element: E) = lock.withLock { elements.contains(element) }
- override fun containsAll(elements: Collection<E>) = lock.withLock {
- this.elements.containsAll(elements)
- }
- override operator fun get(index: Int) = lock.withLock { elements[index] }
- override fun indexOf(element: E): Int = lock.withLock { elements.indexOf(element) }
- override fun lastIndexOf(element: E): Int = lock.withLock { elements.lastIndexOf(element) }
- override fun isEmpty() = lock.withLock { elements.isEmpty() }
- override fun listIterator(index: Int) = ArrayTrackRecordIterator(elements, index, size)
- override fun listIterator() = listIterator(0)
- override fun iterator() = listIterator()
- override fun subList(fromIndex: Int, toIndex: Int): List<E> = lock.withLock {
- elements.subList(fromIndex, toIndex)
- }
-
- // TrackRecord<E> implementation
- override fun add(e: E): Boolean {
- lock.withLock {
- elements.add(e)
- condition.signalAll()
- }
- return true
- }
- override fun poll(timeoutMs: Long, pos: Int, predicate: (E) -> Boolean) = lock.withLock {
- elements.getOrNull(pollForIndexReadLocked(timeoutMs, pos, predicate))
- }
-
- // For convenience
- fun getOrNull(pos: Int, predicate: (E) -> Boolean) = lock.withLock {
- if (pos < 0 || pos > size) null else elements.subList(pos, size).find(predicate)
- }
-
- // Returns the index of the next element whose position is >= pos matching the predicate, if
- // necessary waiting until such a time that such an element is available, with a timeout.
- // If no such element is found within the timeout -1 is returned.
- private fun pollForIndexReadLocked(timeoutMs: Long, pos: Int, predicate: (E) -> Boolean): Int {
- val deadline = System.currentTimeMillis() + timeoutMs
- var index = pos
- do {
- while (index < elements.size) {
- if (predicate(elements[index])) return index
- ++index
- }
- } while (condition.await(deadline - System.currentTimeMillis()))
- return -1
- }
-
- /**
- * Returns a ReadHead over this ArrayTrackRecord. The returned ReadHead is tied to the
- * current thread.
- */
- fun newReadHead() = ReadHead()
-
- /**
- * ReadHead is an object that helps users of ArrayTrackRecord keep track of how far
- * it has read this far in the ArrayTrackRecord. A ReadHead is always associated with
- * a single instance of ArrayTrackRecord. Multiple ReadHeads can be created and used
- * on the same instance of ArrayTrackRecord concurrently, and the ArrayTrackRecord
- * instance can also be used concurrently. ReadHead maintains the current index that is
- * the next to be read, and calls this the "mark".
- *
- * A ReadHead delegates all TrackRecord methods to its associated ArrayTrackRecord, and
- * inherits its thread-safe properties. However, the additional methods that ReadHead
- * offers on top of TrackRecord do not share these properties and can only be used by
- * the thread that created the ReadHead. This is because by construction it does not
- * make sense to use a ReadHead on multiple threads concurrently (see below for details).
- *
- * In a ReadHead, {@link poll(Long, (E) -> Boolean)} works similarly to a LinkedBlockingQueue.
- * It can be called repeatedly and will return the elements as they arrive.
- *
- * Intended usage looks something like this :
- * val TrackRecord<MyObject> record = ArrayTrackRecord().newReadHead()
- * Thread().start {
- * // do stuff
- * record.add(something)
- * // do stuff
- * }
- *
- * val obj1 = record.poll(timeout)
- * // do something with obj1
- * val obj2 = record.poll(timeout)
- * // do something with obj2
- *
- * The point is that the caller does not have to track the mark like it would have to if
- * it was using ArrayTrackRecord directly.
- *
- * Note that if multiple threads were using poll() concurrently on the same ReadHead, what
- * happens to the mark and the return values could be well defined, but it could not
- * be useful because there is no way to provide either a guarantee not to skip objects nor
- * a guarantee about the mark position at the exit of poll(). This is even more true in the
- * presence of a predicate to filter returned elements, because one thread might be
- * filtering out the events the other is interested in.
- * Instead, this use case is supported by creating multiple ReadHeads on the same instance
- * of ArrayTrackRecord. Each ReadHead is then guaranteed to see all events always and
- * guarantees are made on the value of the mark upon return. {@see poll(Long, (E) -> Boolean)}
- * for details. Be careful to create each ReadHead on the thread it is meant to be used on.
- *
- * Users of a ReadHead can ask for the current position of the mark at any time. This mark
- * can be used later to replay the history of events either on this ReadHead, on the associated
- * ArrayTrackRecord or on another ReadHead associated with the same ArrayTrackRecord. It
- * might look like this in the reader thread :
- *
- * val markAtStart = record.mark
- * // Start processing interesting events
- * while (val element = record.poll(timeout) { it.isInteresting() }) {
- * // Do something with element
- * }
- * // Look for stuff that happened while searching for interesting events
- * val firstElementReceived = record.getOrNull(markAtStart)
- * val firstSpecialElement = record.getOrNull(markAtStart) { it.isSpecial() }
- * // Get the first special element since markAtStart, possibly blocking until one is available
- * val specialElement = record.poll(timeout, markAtStart) { it.isSpecial() }
- */
- inner class ReadHead : TrackRecord<E> by this@ArrayTrackRecord {
- private val owningThread = Thread.currentThread()
- private var readHead = 0
-
- /**
- * @return the current value of the mark.
- */
- var mark
- get() = readHead.also { checkThread() }
- set(v: Int) = rewind(v)
- fun rewind(v: Int) {
- checkThread()
- readHead = v
- }
-
- private fun checkThread() = check(Thread.currentThread() == owningThread) {
- "Must be called by the thread that created this object"
- }
-
- /**
- * Returns the first element after the mark, optionally blocking until one is available, or
- * null if no such element can be found within the timeout.
- * If a predicate is given, only elements matching the predicate are returned.
- *
- * Upon return the mark will be set to immediately after the returned element, or after
- * the last element in the queue if null is returned. This means this method will always
- * skip elements that do not match the predicate, even if it returns null.
- *
- * This method can only be used by the thread that created this ManagedRecordingQueue.
- * If used on another thread, this throws IllegalStateException.
- *
- * @param timeoutMs how long, in milliseconds, to wait at most (best effort approximation).
- * @param predicate an optional predicate to filter elements to be returned.
- * @return an element matching the predicate, or null if timeout.
- */
- fun poll(timeoutMs: Long, predicate: (E) -> Boolean = { true }): E? {
- checkThread()
- lock.withLock {
- val index = pollForIndexReadLocked(timeoutMs, readHead, predicate)
- readHead = if (index < 0) size else index + 1
- return getOrNull(index)
- }
- }
-
- /**
- * Returns the first element after the mark or null. This never blocks.
- *
- * This method can only be used by the thread that created this ManagedRecordingQueue.
- * If used on another thread, this throws IllegalStateException.
- */
- fun peek(): E? = getOrNull(readHead).also { checkThread() }
- }
-}
-
-// Private helper
-private fun Condition.await(timeoutMs: Long) = this.await(timeoutMs, TimeUnit.MILLISECONDS)
diff --git a/tests/lib/src/com/android/testutils/ConcurrentIntepreter.kt b/tests/lib/src/com/android/testutils/ConcurrentIntepreter.kt
deleted file mode 100644
index 25b1e0f..0000000
--- a/tests/lib/src/com/android/testutils/ConcurrentIntepreter.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.android.testutils
-
-import android.os.SystemClock
-import java.util.concurrent.CyclicBarrier
-import kotlin.system.measureTimeMillis
-import kotlin.test.assertEquals
-import kotlin.test.assertFails
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-
-// The table contains pairs associating a regexp with the code to run. The statement is matched
-// against each matcher in sequence and when a match is found the associated code is run, passing
-// it the TrackRecord under test and the result of the regexp match.
-typealias InterpretMatcher<T> = Pair<Regex, (ConcurrentIntepreter<T>, T, MatchResult) -> Any?>
-
-// The default unit of time for interpreted tests
-val INTERPRET_TIME_UNIT = 40L // ms
-
-/**
- * A small interpreter for testing parallel code. The interpreter will read a list of lines
- * consisting of "|"-separated statements. Each column runs in a different concurrent thread
- * and all threads wait for each other in between lines. Each statement is split on ";" then
- * matched with regular expressions in the instructionTable constant, which contains the
- * code associated with each statement. The interpreter supports an object being passed to
- * the interpretTestSpec() method to be passed in each lambda (think about the object under
- * test), and an optional transform function to be executed on the object at the start of
- * every thread.
- *
- * The time unit is defined in milliseconds by the interpretTimeUnit member, which has a default
- * value but can be passed to the constructor. Whitespace is ignored.
- *
- * The interpretation table has to be passed as an argument. It's a table associating a regexp
- * with the code that should execute, as a function taking three arguments : the interpreter,
- * the regexp match, and the object. See the individual tests for the DSL of that test.
- * Implementors for new interpreting languages are encouraged to look at the defaultInterpretTable
- * constant below for an example of how to write an interpreting table.
- * Some expressions already exist by default and can be used by all interpreters. They include :
- * sleep(x) : sleeps for x time units and returns Unit ; sleep alone means sleep(1)
- * EXPR = VALUE : asserts that EXPR equals VALUE. EXPR is interpreted. VALUE can either be the
- * string "null" or an int. Returns Unit.
- * EXPR time x..y : measures the time taken by EXPR and asserts it took at least x and at most
- * y time units.
- * EXPR // any text : comments are ignored.
- */
-open class ConcurrentIntepreter<T>(
- localInterpretTable: List<InterpretMatcher<T>>,
- val interpretTimeUnit: Long = INTERPRET_TIME_UNIT
-) {
- private val interpretTable: List<InterpretMatcher<T>> =
- localInterpretTable + getDefaultInstructions()
-
- // Split the line into multiple statements separated by ";" and execute them. Return whatever
- // the last statement returned.
- fun interpretMultiple(instr: String, r: T): Any? {
- return instr.split(";").map { interpret(it.trim(), r) }.last()
- }
-
- // Match the statement to a regex and interpret it.
- fun interpret(instr: String, r: T): Any? {
- val (matcher, code) =
- interpretTable.find { instr matches it.first } ?: throw SyntaxException(instr)
- val match = matcher.matchEntire(instr) ?: throw SyntaxException(instr)
- return code(this, r, match)
- }
-
- // Spins as many threads as needed by the test spec and interpret each program concurrently,
- // having all threads waiting on a CyclicBarrier after each line.
- // |lineShift| says how many lines after the call the spec starts. This is used for error
- // reporting. Unfortunately AFAICT there is no way to get the line of an argument rather
- // than the line at which the expression starts.
- fun interpretTestSpec(
- spec: String,
- initial: T,
- lineShift: Int = 0,
- threadTransform: (T) -> T = { it }
- ) {
- // For nice stack traces
- val callSite = getCallingMethod()
- val lines = spec.trim().trim('\n').split("\n").map { it.split("|") }
- // |threads| contains arrays of strings that make up the statements of a thread : in other
- // words, it's an array that contains a list of statements for each column in the spec.
- val threadCount = lines[0].size
- assertTrue(lines.all { it.size == threadCount })
- val threadInstructions = (0 until threadCount).map { i -> lines.map { it[i].trim() } }
- val barrier = CyclicBarrier(threadCount)
- var crash: InterpretException? = null
- threadInstructions.mapIndexed { threadIndex, instructions ->
- Thread {
- val threadLocal = threadTransform(initial)
- barrier.await()
- var lineNum = 0
- instructions.forEach {
- if (null != crash) return@Thread
- lineNum += 1
- try {
- interpretMultiple(it, threadLocal)
- } catch (e: Throwable) {
- // If fail() or some exception was called, the thread will come here ; if
- // the exception isn't caught the process will crash, which is not nice for
- // testing. Instead, catch the exception, cancel other threads, and report
- // nicely. Catch throwable because fail() is AssertionError, which inherits
- // from Error.
- crash = InterpretException(threadIndex, it,
- callSite.lineNumber + lineNum + lineShift,
- callSite.className, callSite.methodName, callSite.fileName, e)
- }
- barrier.await()
- }
- }.also { it.start() }
- }.forEach { it.join() }
- // If the test failed, crash with line number
- crash?.let { throw it }
- }
-
- // Helper to get the stack trace for a calling method
- private fun getCallingStackTrace(): Array<StackTraceElement> {
- try {
- throw RuntimeException()
- } catch (e: RuntimeException) {
- return e.stackTrace
- }
- }
-
- // Find the calling method. This is the first method in the stack trace that is annotated
- // with @Test.
- fun getCallingMethod(): StackTraceElement {
- val stackTrace = getCallingStackTrace()
- return stackTrace.find { element ->
- val clazz = Class.forName(element.className)
- // Because the stack trace doesn't list the formal arguments, find all methods with
- // this name and return this name if any of them is annotated with @Test.
- clazz.declaredMethods
- .filter { method -> method.name == element.methodName }
- .any { method -> method.getAnnotation(org.junit.Test::class.java) != null }
- } ?: stackTrace[3]
- // If no method is annotated return the 4th one, because that's what it usually is :
- // 0 is getCallingStackTrace, 1 is this method, 2 is ConcurrentInterpreter#interpretTestSpec
- }
-}
-
-private fun <T> getDefaultInstructions() = listOf<InterpretMatcher<T>>(
- // Interpret an empty line as doing nothing.
- Regex("") to { _, _, _ -> null },
- // Ignore comments.
- Regex("(.*)//.*") to { i, t, r -> i.interpret(r.strArg(1), t) },
- // Interpret "XXX time x..y" : run XXX and check it took at least x and not more than y
- Regex("""(.*)\s*time\s*(\d+)\.\.(\d+)""") to { i, t, r ->
- val time = measureTimeMillis { i.interpret(r.strArg(1), t) }
- assertTrue(time in r.timeArg(2)..r.timeArg(3), "$time not in ${r.timeArg(2)..r.timeArg(3)}")
- },
- // Interpret "XXX = YYY" : run XXX and assert its return value is equal to YYY. "null" supported
- Regex("""(.*)\s*=\s*(null|\d+)""") to { i, t, r ->
- i.interpret(r.strArg(1), t).also {
- if ("null" == r.strArg(2)) assertNull(it) else assertEquals(r.intArg(2), it)
- }
- },
- // Interpret sleep. Optional argument for the count, in INTERPRET_TIME_UNIT units.
- Regex("""sleep(\((\d+)\))?""") to { i, t, r ->
- SystemClock.sleep(if (r.strArg(2).isEmpty()) i.interpretTimeUnit else r.timeArg(2))
- },
- Regex("""(.*)\s*fails""") to { i, t, r ->
- assertFails { i.interpret(r.strArg(1), t) }
- }
-)
-
-class SyntaxException(msg: String, cause: Throwable? = null) : RuntimeException(msg, cause)
-class InterpretException(
- threadIndex: Int,
- instr: String,
- lineNum: Int,
- className: String,
- methodName: String,
- fileName: String,
- cause: Throwable
-) : RuntimeException("Failure: $instr", cause) {
- init {
- stackTrace = arrayOf(StackTraceElement(
- className,
- "$methodName:thread$threadIndex",
- fileName,
- lineNum)) + super.getStackTrace()
- }
-}
-
-// Some small helpers to avoid to say the large ".groupValues[index].trim()" every time
-fun MatchResult.strArg(index: Int) = this.groupValues[index].trim()
-fun MatchResult.intArg(index: Int) = strArg(index).toInt()
-fun MatchResult.timeArg(index: Int) = INTERPRET_TIME_UNIT * intArg(index)
diff --git a/tests/lib/src/com/android/testutils/DevSdkIgnoreRule.kt b/tests/lib/src/com/android/testutils/DevSdkIgnoreRule.kt
deleted file mode 100644
index 4a83f6f..0000000
--- a/tests/lib/src/com/android/testutils/DevSdkIgnoreRule.kt
+++ /dev/null
@@ -1,90 +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.testutils
-
-import android.os.Build
-import org.junit.Assume.assumeTrue
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-
-/**
- * Returns true if the development SDK version of the device is in the provided range.
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher than
- * [Build.VERSION.SDK_INT].
- */
-fun isDevSdkInRange(minExclusive: Int?, maxInclusive: Int?): Boolean {
- // In-development API n+1 will have SDK_INT == n and CODENAME != REL.
- // Stable API n has SDK_INT == n and CODENAME == REL.
- val release = "REL" == Build.VERSION.CODENAME
- val sdkInt = Build.VERSION.SDK_INT
- val devApiLevel = sdkInt + if (release) 0 else 1
-
- return (minExclusive == null || devApiLevel > minExclusive) &&
- (maxInclusive == null || devApiLevel <= maxInclusive)
-}
-
-/**
- * A test rule to ignore tests based on the development SDK level.
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher than
- * [Build.VERSION.SDK_INT].
- *
- * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
- * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
- */
-class DevSdkIgnoreRule @JvmOverloads constructor(
- private val ignoreClassUpTo: Int? = null,
- private val ignoreClassAfter: Int? = null
-) : TestRule {
- override fun apply(base: Statement, description: Description): Statement {
- return IgnoreBySdkStatement(base, description)
- }
-
- /**
- * Ignore the test for any development SDK that is strictly after [value].
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher
- * than [Build.VERSION.SDK_INT].
- */
- annotation class IgnoreAfter(val value: Int)
-
- /**
- * Ignore the test for any development SDK that lower than or equal to [value].
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher
- * than [Build.VERSION.SDK_INT].
- */
- annotation class IgnoreUpTo(val value: Int)
-
- private inner class IgnoreBySdkStatement(
- private val base: Statement,
- private val description: Description
- ) : Statement() {
- override fun evaluate() {
- val ignoreAfter = description.getAnnotation(IgnoreAfter::class.java)
- val ignoreUpTo = description.getAnnotation(IgnoreUpTo::class.java)
-
- val message = "Skipping test for build ${Build.VERSION.CODENAME} " +
- "with SDK ${Build.VERSION.SDK_INT}"
- assumeTrue(message, isDevSdkInRange(ignoreClassUpTo, ignoreClassAfter))
- assumeTrue(message, isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value))
- base.evaluate()
- }
- }
-}
\ No newline at end of file
diff --git a/tests/lib/src/com/android/testutils/DevSdkIgnoreRunner.kt b/tests/lib/src/com/android/testutils/DevSdkIgnoreRunner.kt
deleted file mode 100644
index 73b2843..0000000
--- a/tests/lib/src/com/android/testutils/DevSdkIgnoreRunner.kt
+++ /dev/null
@@ -1,72 +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.testutils
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
-import org.junit.runner.Description
-import org.junit.runner.Runner
-import org.junit.runner.notification.RunNotifier
-
-/**
- * A runner that can skip tests based on the development SDK as defined in [DevSdkIgnoreRule].
- *
- * Generally [DevSdkIgnoreRule] should be used for that purpose (using rules is preferable over
- * replacing the test runner), however JUnit runners inspect all methods in the test class before
- * processing test rules. This may cause issues if the test methods are referencing classes that do
- * not exist on the SDK of the device the test is run on.
- *
- * This runner inspects [IgnoreAfter] and [IgnoreUpTo] annotations on the test class, and will skip
- * the whole class if they do not match the development SDK as defined in [DevSdkIgnoreRule].
- * Otherwise, it will delegate to [AndroidJUnit4] to run the test as usual.
- *
- * Example usage:
- *
- * @RunWith(DevSdkIgnoreRunner::class)
- * @IgnoreUpTo(Build.VERSION_CODES.Q)
- * class MyTestClass { ... }
- */
-class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner() {
- private val baseRunner = klass.let {
- val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java)
- val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java)
-
- if (isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value)) AndroidJUnit4(klass) else null
- }
-
- override fun run(notifier: RunNotifier) {
- if (baseRunner != null) {
- baseRunner.run(notifier)
- return
- }
-
- // Report a single, skipped placeholder test for this class, so that the class is still
- // visible as skipped in test results.
- notifier.fireTestIgnored(
- Description.createTestDescription(klass, "skippedClassForDevSdkMismatch"))
- }
-
- override fun getDescription(): Description {
- return baseRunner?.description ?: Description.createSuiteDescription(klass)
- }
-
- override fun testCount(): Int {
- // When ignoring the tests, a skipped placeholder test is reported, so test count is 1.
- return baseRunner?.testCount() ?: 1
- }
-}
\ No newline at end of file
diff --git a/tests/lib/src/com/android/testutils/FakeDns.kt b/tests/lib/src/com/android/testutils/FakeDns.kt
deleted file mode 100644
index 825d748..0000000
--- a/tests/lib/src/com/android/testutils/FakeDns.kt
+++ /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.testutils
-
-import android.net.DnsResolver
-import android.net.InetAddresses
-import android.os.Looper
-import android.os.Handler
-import com.android.internal.annotations.GuardedBy
-import java.net.InetAddress
-import java.util.concurrent.Executor
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.doAnswer
-
-const val TYPE_UNSPECIFIED = -1
-// TODO: Integrate with NetworkMonitorTest.
-class FakeDns(val mockResolver: DnsResolver) {
- class DnsEntry(val hostname: String, val type: Int, val addresses: List<InetAddress>) {
- fun match(host: String, type: Int) = hostname.equals(host) && type == type
- }
-
- @GuardedBy("answers")
- val answers = ArrayList<DnsEntry>()
-
- fun getAnswer(hostname: String, type: Int): DnsEntry? = synchronized(answers) {
- return answers.firstOrNull { it.match(hostname, type) }
- }
-
- fun setAnswer(hostname: String, answer: Array<String>, type: Int) = synchronized(answers) {
- val ans = DnsEntry(hostname, type, generateAnswer(answer))
- // Replace or remove the existing one.
- when (val index = answers.indexOfFirst { it.match(hostname, type) }) {
- -1 -> answers.add(ans)
- else -> answers[index] = ans
- }
- }
-
- private fun generateAnswer(answer: Array<String>) =
- answer.filterNotNull().map { InetAddresses.parseNumericAddress(it) }
-
- fun startMocking() {
- // Mock DnsResolver.query() w/o type
- doAnswer {
- mockAnswer(it, 1, -1, 3, 5)
- }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* flags */,
- any() /* executor */, any() /* cancellationSignal */, any() /*callback*/)
- // Mock DnsResolver.query() w/ type
- doAnswer {
- mockAnswer(it, 1, 2, 4, 6)
- }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* nsType */,
- anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */,
- any() /*callback*/)
- }
-
- private fun mockAnswer(
- it: InvocationOnMock,
- posHos: Int,
- posType: Int,
- posExecutor: Int,
- posCallback: Int
- ) {
- val hostname = it.arguments[posHos] as String
- val executor = it.arguments[posExecutor] as Executor
- val callback = it.arguments[posCallback] as DnsResolver.Callback<List<InetAddress>>
- var type = if (posType != -1) it.arguments[posType] as Int else TYPE_UNSPECIFIED
- val answer = getAnswer(hostname, type)
-
- if (!answer?.addresses.isNullOrEmpty()) {
- Handler(Looper.getMainLooper()).post({ executor.execute({
- callback.onAnswer(answer?.addresses, 0); }) })
- }
- }
-
- /** Clears all entries. */
- fun clearAll() = synchronized(answers) {
- answers.clear()
- }
-}
diff --git a/tests/lib/src/com/android/testutils/HandlerUtils.kt b/tests/lib/src/com/android/testutils/HandlerUtils.kt
deleted file mode 100644
index fa36209..0000000
--- a/tests/lib/src/com/android/testutils/HandlerUtils.kt
+++ /dev/null
@@ -1,49 +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.testutils
-
-import android.os.ConditionVariable
-import android.os.Handler
-import android.os.HandlerThread
-import java.util.concurrent.Executor
-import kotlin.system.measureTimeMillis
-import kotlin.test.fail
-
-/**
- * Block until the specified Handler or HandlerThread becomes idle, or until timeoutMs has passed.
- */
-fun HandlerThread.waitForIdle(timeoutMs: Int) = threadHandler.waitForIdle(timeoutMs.toLong())
-fun HandlerThread.waitForIdle(timeoutMs: Long) = threadHandler.waitForIdle(timeoutMs)
-fun Handler.waitForIdle(timeoutMs: Int) = waitForIdle(timeoutMs.toLong())
-fun Handler.waitForIdle(timeoutMs: Long) {
- val cv = ConditionVariable(false)
- post(cv::open)
- if (!cv.block(timeoutMs)) {
- fail("Handler did not become idle after ${timeoutMs}ms")
- }
-}
-
-/**
- * Block until the given Serial Executor becomes idle, or until timeoutMs has passed.
- */
-fun waitForIdleSerialExecutor(executor: Executor, timeoutMs: Long) {
- val cv = ConditionVariable()
- executor.execute(cv::open)
- if (!cv.block(timeoutMs)) {
- fail("Executor did not become idle after ${timeoutMs}ms")
- }
-}
diff --git a/tests/lib/src/com/android/testutils/NetworkStatsUtils.kt b/tests/lib/src/com/android/testutils/NetworkStatsUtils.kt
deleted file mode 100644
index 8324b25..0000000
--- a/tests/lib/src/com/android/testutils/NetworkStatsUtils.kt
+++ /dev/null
@@ -1,78 +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.testutils
-
-import android.net.NetworkStats
-import kotlin.test.assertTrue
-
-@JvmOverloads
-fun orderInsensitiveEquals(
- leftStats: NetworkStats,
- rightStats: NetworkStats,
- compareTime: Boolean = false
-): Boolean {
- if (leftStats == rightStats) return true
- if (compareTime && leftStats.getElapsedRealtime() != rightStats.getElapsedRealtime()) {
- return false
- }
-
- // While operations such as add/subtract will preserve empty entries. This will make
- // the result be hard to verify during test. Remove them before comparing since they
- // are not really affect correctness.
- // TODO (b/152827872): Remove empty entries after addition/subtraction.
- val leftTrimmedEmpty = leftStats.removeEmptyEntries()
- val rightTrimmedEmpty = rightStats.removeEmptyEntries()
-
- if (leftTrimmedEmpty.size() != rightTrimmedEmpty.size()) return false
- val left = NetworkStats.Entry()
- val right = NetworkStats.Entry()
- // Order insensitive compare.
- for (i in 0 until leftTrimmedEmpty.size()) {
- leftTrimmedEmpty.getValues(i, left)
- val j: Int = rightTrimmedEmpty.findIndexHinted(left.iface, left.uid, left.set, left.tag,
- left.metered, left.roaming, left.defaultNetwork, i)
- if (j == -1) return false
- rightTrimmedEmpty.getValues(j, right)
- if (left != right) return false
- }
- return true
-}
-
-/**
- * Assert that two {@link NetworkStats} are equals, assuming the order of the records are not
- * necessarily the same.
- *
- * @note {@code elapsedRealtime} is not compared by default, given that in test cases that is not
- * usually used.
- */
-@JvmOverloads
-fun assertNetworkStatsEquals(
- expected: NetworkStats,
- actual: NetworkStats,
- compareTime: Boolean = false
-) {
- assertTrue(orderInsensitiveEquals(expected, actual, compareTime),
- "expected: " + expected + " but was: " + actual)
-}
-
-/**
- * Assert that after being parceled then unparceled, {@link NetworkStats} is equal to the original
- * object.
- */
-fun assertParcelingIsLossless(stats: NetworkStats) {
- assertParcelingIsLossless(stats, { a, b -> orderInsensitiveEquals(a, b) })
-}
diff --git a/tests/lib/src/com/android/testutils/ParcelUtils.kt b/tests/lib/src/com/android/testutils/ParcelUtils.kt
deleted file mode 100644
index 5784f7c..0000000
--- a/tests/lib/src/com/android/testutils/ParcelUtils.kt
+++ /dev/null
@@ -1,68 +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.testutils
-
-import android.os.Parcel
-import android.os.Parcelable
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-/**
- * Return a new instance of `T` after being parceled then unparceled.
- */
-fun <T : Parcelable> parcelingRoundTrip(source: T): T {
- val creator: Parcelable.Creator<T>
- try {
- creator = source.javaClass.getField("CREATOR").get(null) as Parcelable.Creator<T>
- } catch (e: IllegalAccessException) {
- fail("Missing CREATOR field: " + e.message)
- } catch (e: NoSuchFieldException) {
- fail("Missing CREATOR field: " + e.message)
- }
-
- var p = Parcel.obtain()
- source.writeToParcel(p, /* flags */ 0)
- p.setDataPosition(0)
- val marshalled = p.marshall()
- p = Parcel.obtain()
- p.unmarshall(marshalled, 0, marshalled.size)
- p.setDataPosition(0)
- return creator.createFromParcel(p)
-}
-
-/**
- * Assert that after being parceled then unparceled, `source` is equal to the original
- * object. If a customized equals function is provided, uses the provided one.
- */
-@JvmOverloads
-fun <T : Parcelable> assertParcelingIsLossless(
- source: T,
- equals: (T, T) -> Boolean = { a, b -> a == b }
-) {
- val actual = parcelingRoundTrip(source)
- assertTrue(equals(source, actual), "Expected $source, but was $actual")
-}
-
-@JvmOverloads
-fun <T : Parcelable> assertParcelSane(
- obj: T,
- fieldCount: Int,
- equals: (T, T) -> Boolean = { a, b -> a == b }
-) {
- assertFieldCountEquals(fieldCount, obj::class.java)
- assertParcelingIsLossless(obj, equals)
-}
diff --git a/tests/lib/src/com/android/testutils/TapPacketReader.java b/tests/lib/src/com/android/testutils/TapPacketReader.java
deleted file mode 100644
index e55ed44..0000000
--- a/tests/lib/src/com/android/testutils/TapPacketReader.java
+++ /dev/null
@@ -1,83 +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.testutils;
-
-import android.net.util.PacketReader;
-import android.os.Handler;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.function.Predicate;
-
-import kotlin.Lazy;
-import kotlin.LazyKt;
-
-public class TapPacketReader extends PacketReader {
- private final FileDescriptor mTapFd;
- private final ArrayTrackRecord<byte[]> mReceivedPackets = new ArrayTrackRecord<>();
- private final Lazy<ArrayTrackRecord<byte[]>.ReadHead> mReadHead =
- LazyKt.lazy(mReceivedPackets::newReadHead);
-
- public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) {
- super(h, maxPacketSize);
- mTapFd = tapFd;
- }
-
- @Override
- protected FileDescriptor createFd() {
- return mTapFd;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final byte[] newPacket = Arrays.copyOf(recvbuf, length);
- if (!mReceivedPackets.add(newPacket)) {
- throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!");
- }
- }
-
- /**
- * Get the next packet that was received on the interface.
- */
- @Nullable
- public byte[] popPacket(long timeoutMs) {
- return mReadHead.getValue().poll(timeoutMs, packet -> true);
- }
-
- /**
- * Get the next packet that was received on the interface and matches the specified filter.
- */
- @Nullable
- public byte[] popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter) {
- return mReadHead.getValue().poll(timeoutMs, filter::test);
- }
-
- public void sendResponse(final ByteBuffer packet) throws IOException {
- try (FileOutputStream out = new FileOutputStream(mTapFd)) {
- byte[] packetBytes = new byte[packet.limit()];
- packet.get(packetBytes);
- packet.flip(); // So we can reuse it in the future.
- out.write(packetBytes);
- }
- }
-}
diff --git a/tests/lib/src/com/android/testutils/TestNetworkTracker.kt b/tests/lib/src/com/android/testutils/TestNetworkTracker.kt
deleted file mode 100644
index 4bd9ae8..0000000
--- a/tests/lib/src/com/android/testutils/TestNetworkTracker.kt
+++ /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.testutils
-
-import android.content.Context
-import android.net.ConnectivityManager
-import android.net.ConnectivityManager.NetworkCallback
-import android.net.LinkAddress
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import android.net.StringNetworkSpecifier
-import android.net.TestNetworkInterface
-import android.net.TestNetworkManager
-import android.os.Binder
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-
-/**
- * Create a test network based on a TUN interface.
- *
- * This method will block until the test network is available. Requires
- * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
- * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
- */
-fun initTestNetwork(context: Context, interfaceAddr: LinkAddress, setupTimeoutMs: Long = 10_000L):
- TestNetworkTracker {
- val tnm = context.getSystemService(TestNetworkManager::class.java)
- val iface = tnm.createTunInterface(arrayOf(interfaceAddr))
- return TestNetworkTracker(context, iface, tnm, setupTimeoutMs)
-}
-
-/**
- * Utility class to create and track test networks.
- *
- * This class is not thread-safe.
- */
-class TestNetworkTracker internal constructor(
- val context: Context,
- val iface: TestNetworkInterface,
- tnm: TestNetworkManager,
- setupTimeoutMs: Long
-) {
- private val cm = context.getSystemService(ConnectivityManager::class.java)
- private val binder = Binder()
-
- private val networkCallback: NetworkCallback
- val network: Network
-
- init {
- val networkFuture = CompletableFuture<Network>()
- val networkRequest = NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- // Test networks do not have NOT_VPN or TRUSTED capabilities by default
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .setNetworkSpecifier(StringNetworkSpecifier(iface.interfaceName))
- .build()
- networkCallback = object : NetworkCallback() {
- override fun onAvailable(network: Network) {
- networkFuture.complete(network)
- }
- }
- cm.requestNetwork(networkRequest, networkCallback)
-
- try {
- tnm.setupTestNetwork(iface.interfaceName, binder)
- network = networkFuture.get(setupTimeoutMs, TimeUnit.MILLISECONDS)
- } catch (e: Throwable) {
- teardown()
- throw e
- }
- }
-
- fun teardown() {
- cm.unregisterNetworkCallback(networkCallback)
- }
-}
\ No newline at end of file
diff --git a/tests/lib/src/com/android/testutils/TestableNetworkCallback.kt b/tests/lib/src/com/android/testutils/TestableNetworkCallback.kt
deleted file mode 100644
index b63e137..0000000
--- a/tests/lib/src/com/android/testutils/TestableNetworkCallback.kt
+++ /dev/null
@@ -1,392 +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.testutils
-
-import android.net.ConnectivityManager.NetworkCallback
-import android.net.LinkProperties
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import com.android.testutils.RecorderCallback.CallbackEntry.Available
-import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
-import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
-import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
-import com.android.testutils.RecorderCallback.CallbackEntry.Losing
-import com.android.testutils.RecorderCallback.CallbackEntry.Lost
-import com.android.testutils.RecorderCallback.CallbackEntry.Resumed
-import com.android.testutils.RecorderCallback.CallbackEntry.Suspended
-import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
-import kotlin.reflect.KClass
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-object NULL_NETWORK : Network(-1)
-object ANY_NETWORK : Network(-2)
-
-private val Int.capabilityName get() = NetworkCapabilities.capabilityNameOf(this)
-
-open class RecorderCallback private constructor(
- private val backingRecord: ArrayTrackRecord<CallbackEntry>
-) : NetworkCallback() {
- public constructor() : this(ArrayTrackRecord())
- protected constructor(src: RecorderCallback?): this(src?.backingRecord ?: ArrayTrackRecord())
-
- sealed class CallbackEntry {
- // To get equals(), hashcode(), componentN() etc for free, the child classes of
- // this class are data classes. But while data classes can inherit from other classes,
- // they may only have visible members in the constructors, so they couldn't declare
- // a constructor with a non-val arg to pass to CallbackEntry. Instead, force all
- // subclasses to implement a `network' property, which can be done in a data class
- // constructor by specifying override.
- abstract val network: Network
-
- data class Available(override val network: Network) : CallbackEntry()
- data class CapabilitiesChanged(
- override val network: Network,
- val caps: NetworkCapabilities
- ) : CallbackEntry()
- data class LinkPropertiesChanged(
- override val network: Network,
- val lp: LinkProperties
- ) : CallbackEntry()
- data class Suspended(override val network: Network) : CallbackEntry()
- data class Resumed(override val network: Network) : CallbackEntry()
- data class Losing(override val network: Network, val maxMsToLive: Int) : CallbackEntry()
- data class Lost(override val network: Network) : CallbackEntry()
- data class Unavailable private constructor(
- override val network: Network
- ) : CallbackEntry() {
- constructor() : this(NULL_NETWORK)
- }
- data class BlockedStatus(
- override val network: Network,
- val blocked: Boolean
- ) : CallbackEntry()
-
- // Convenience constants for expecting a type
- companion object {
- @JvmField
- val AVAILABLE = Available::class
- @JvmField
- val NETWORK_CAPS_UPDATED = CapabilitiesChanged::class
- @JvmField
- val LINK_PROPERTIES_CHANGED = LinkPropertiesChanged::class
- @JvmField
- val SUSPENDED = Suspended::class
- @JvmField
- val RESUMED = Resumed::class
- @JvmField
- val LOSING = Losing::class
- @JvmField
- val LOST = Lost::class
- @JvmField
- val UNAVAILABLE = Unavailable::class
- @JvmField
- val BLOCKED_STATUS = BlockedStatus::class
- }
- }
-
- val history = backingRecord.newReadHead()
- val mark get() = history.mark
-
- override fun onAvailable(network: Network) {
- history.add(Available(network))
- }
-
- // PreCheck is not used in the tests today. For backward compatibility with existing tests that
- // expect the callbacks not to record this, do not listen to PreCheck here.
-
- override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
- history.add(CapabilitiesChanged(network, caps))
- }
-
- override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
- history.add(LinkPropertiesChanged(network, lp))
- }
-
- override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
- history.add(BlockedStatus(network, blocked))
- }
-
- override fun onNetworkSuspended(network: Network) {
- history.add(Suspended(network))
- }
-
- override fun onNetworkResumed(network: Network) {
- history.add(Resumed(network))
- }
-
- override fun onLosing(network: Network, maxMsToLive: Int) {
- history.add(Losing(network, maxMsToLive))
- }
-
- override fun onLost(network: Network) {
- history.add(Lost(network))
- }
-
- override fun onUnavailable() {
- history.add(Unavailable())
- }
-}
-
-private const val DEFAULT_TIMEOUT = 200L // ms
-
-open class TestableNetworkCallback private constructor(
- src: TestableNetworkCallback?,
- val defaultTimeoutMs: Long = DEFAULT_TIMEOUT
-) : RecorderCallback(src) {
- @JvmOverloads
- constructor(timeoutMs: Long = DEFAULT_TIMEOUT): this(null, timeoutMs)
-
- fun createLinkedCopy() = TestableNetworkCallback(this, defaultTimeoutMs)
-
- // The last available network, or null if any network was lost since the last call to
- // onAvailable. TODO : fix this by fixing the tests that rely on this behavior
- val lastAvailableNetwork: Network?
- get() = when (val it = history.lastOrNull { it is Available || it is Lost }) {
- is Available -> it.network
- else -> null
- }
-
- fun pollForNextCallback(timeoutMs: Long = defaultTimeoutMs): CallbackEntry {
- return history.poll(timeoutMs) ?: fail("Did not receive callback after ${timeoutMs}ms")
- }
-
- // Make open for use in ConnectivityServiceTest which is the only one knowing its handlers.
- @JvmOverloads
- open fun assertNoCallback(timeoutMs: Long = defaultTimeoutMs) {
- val cb = history.poll(timeoutMs)
- if (null != cb) fail("Expected no callback but got $cb")
- }
-
- // Expects a callback of the specified type on the specified network within the timeout.
- // If no callback arrives, or a different callback arrives, fail. Returns the callback.
- inline fun <reified T : CallbackEntry> expectCallback(
- network: Network = ANY_NETWORK,
- timeoutMs: Long = defaultTimeoutMs
- ): T = pollForNextCallback(timeoutMs).let {
- if (it !is T || (ANY_NETWORK !== network && it.network != network)) {
- fail("Unexpected callback : $it, expected ${T::class} with Network[$network]")
- } else {
- it
- }
- }
-
- // Expects a callback of the specified type matching the predicate within the timeout.
- // Any callback that doesn't match the predicate will be skipped. Fails only if
- // no matching callback is received within the timeout.
- inline fun <reified T : CallbackEntry> eventuallyExpect(
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- crossinline predicate: (T) -> Boolean = { true }
- ): T = eventuallyExpectOrNull(timeoutMs, from, predicate).also {
- assertNotNull(it, "Callback ${T::class} not received within ${timeoutMs}ms")
- } as T
-
- // TODO (b/157405399) straighten and unify the method names
- inline fun <reified T : CallbackEntry> eventuallyExpectOrNull(
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- crossinline predicate: (T) -> Boolean = { true }
- ) = history.poll(timeoutMs, from) { it is T && predicate(it) } as T?
-
- fun expectCallbackThat(
- timeoutMs: Long = defaultTimeoutMs,
- valid: (CallbackEntry) -> Boolean
- ) = pollForNextCallback(timeoutMs).also { assertTrue(valid(it), "Unexpected callback : $it") }
-
- fun expectCapabilitiesThat(
- net: Network,
- tmt: Long = defaultTimeoutMs,
- valid: (NetworkCapabilities) -> Boolean
- ): CapabilitiesChanged {
- return expectCallback<CapabilitiesChanged>(net, tmt).also {
- assertTrue(valid(it.caps), "Capabilities don't match expectations ${it.caps}")
- }
- }
-
- fun expectLinkPropertiesThat(
- net: Network,
- tmt: Long = defaultTimeoutMs,
- valid: (LinkProperties) -> Boolean
- ): LinkPropertiesChanged {
- return expectCallback<LinkPropertiesChanged>(net, tmt).also {
- assertTrue(valid(it.lp), "LinkProperties don't match expectations ${it.lp}")
- }
- }
-
- // Expects onAvailable and the callbacks that follow it. These are:
- // - onSuspended, iff the network was suspended when the callbacks fire.
- // - onCapabilitiesChanged.
- // - onLinkPropertiesChanged.
- // - onBlockedStatusChanged.
- //
- // @param network the network to expect the callbacks on.
- // @param suspended whether to expect a SUSPENDED callback.
- // @param validated the expected value of the VALIDATED capability in the
- // onCapabilitiesChanged callback.
- // @param tmt how long to wait for the callbacks.
- fun expectAvailableCallbacks(
- net: Network,
- suspended: Boolean = false,
- validated: Boolean = true,
- blocked: Boolean = false,
- tmt: Long = defaultTimeoutMs
- ) {
- expectCallback<Available>(net, tmt)
- if (suspended) {
- expectCallback<Suspended>(net, tmt)
- }
- expectCapabilitiesThat(net, tmt) { validated == it.hasCapability(NET_CAPABILITY_VALIDATED) }
- expectCallback<LinkPropertiesChanged>(net, tmt)
- expectBlockedStatusCallback(blocked, net)
- }
-
- // Backward compatibility for existing Java code. Use named arguments instead and remove all
- // these when there is no user left.
- fun expectAvailableAndSuspendedCallbacks(
- net: Network,
- validated: Boolean,
- tmt: Long = defaultTimeoutMs
- ) = expectAvailableCallbacks(net, suspended = true, validated = validated, tmt = tmt)
-
- fun expectBlockedStatusCallback(blocked: Boolean, net: Network, tmt: Long = defaultTimeoutMs) {
- expectCallback<BlockedStatus>(net, tmt).also {
- assertEquals(it.blocked, blocked, "Unexpected blocked status ${it.blocked}")
- }
- }
-
- // Expects the available callbacks (where the onCapabilitiesChanged must contain the
- // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
- // one we just sent.
- // TODO: this is likely a bug. Fix it and remove this method.
- fun expectAvailableDoubleValidatedCallbacks(net: Network, tmt: Long = defaultTimeoutMs) {
- val mark = history.mark
- expectAvailableCallbacks(net, tmt = tmt)
- val firstCaps = history.poll(tmt, mark) { it is CapabilitiesChanged }
- assertEquals(firstCaps, expectCallback<CapabilitiesChanged>(net, tmt))
- }
-
- // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
- // then expects another onCapabilitiesChanged that has the validated bit set. This is used
- // when a network connects and satisfies a callback, and then immediately validates.
- fun expectAvailableThenValidatedCallbacks(net: Network, tmt: Long = defaultTimeoutMs) {
- expectAvailableCallbacks(net, validated = false, tmt = tmt)
- expectCapabilitiesThat(net, tmt) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
- }
-
- // Temporary Java compat measure : have MockNetworkAgent implement this so that all existing
- // calls with networkAgent can be routed through here without moving MockNetworkAgent.
- // TODO: clean this up, remove this method.
- interface HasNetwork {
- val network: Network
- }
-
- @JvmOverloads
- open fun <T : CallbackEntry> expectCallback(
- type: KClass<T>,
- n: Network?,
- timeoutMs: Long = defaultTimeoutMs
- ) = pollForNextCallback(timeoutMs).also {
- val network = n ?: NULL_NETWORK
- // TODO : remove this .java access if the tests ever use kotlin-reflect. At the time of
- // this writing this would be the only use of this library in the tests.
- assertTrue(type.java.isInstance(it) && it.network == network,
- "Unexpected callback : $it, expected ${type.java} with Network[$network]")
- } as T
-
- @JvmOverloads
- open fun <T : CallbackEntry> expectCallback(
- type: KClass<T>,
- n: HasNetwork?,
- timeoutMs: Long = defaultTimeoutMs
- ) = expectCallback(type, n?.network, timeoutMs)
-
- fun expectAvailableCallbacks(
- n: HasNetwork,
- suspended: Boolean,
- validated: Boolean,
- blocked: Boolean,
- timeoutMs: Long
- ) = expectAvailableCallbacks(n.network, suspended, validated, blocked, timeoutMs)
-
- fun expectAvailableAndSuspendedCallbacks(n: HasNetwork, expectValidated: Boolean) {
- expectAvailableAndSuspendedCallbacks(n.network, expectValidated)
- }
-
- fun expectAvailableCallbacksValidated(n: HasNetwork) {
- expectAvailableCallbacks(n.network)
- }
-
- fun expectAvailableCallbacksValidatedAndBlocked(n: HasNetwork) {
- expectAvailableCallbacks(n.network, blocked = true)
- }
-
- fun expectAvailableCallbacksUnvalidated(n: HasNetwork) {
- expectAvailableCallbacks(n.network, validated = false)
- }
-
- fun expectAvailableCallbacksUnvalidatedAndBlocked(n: HasNetwork) {
- expectAvailableCallbacks(n.network, validated = false, blocked = true)
- }
-
- fun expectAvailableDoubleValidatedCallbacks(n: HasNetwork) {
- expectAvailableDoubleValidatedCallbacks(n.network, defaultTimeoutMs)
- }
-
- fun expectAvailableThenValidatedCallbacks(n: HasNetwork) {
- expectAvailableThenValidatedCallbacks(n.network, defaultTimeoutMs)
- }
-
- @JvmOverloads
- fun expectLinkPropertiesThat(
- n: HasNetwork,
- tmt: Long = defaultTimeoutMs,
- valid: (LinkProperties) -> Boolean
- ) = expectLinkPropertiesThat(n.network, tmt, valid)
-
- @JvmOverloads
- fun expectCapabilitiesThat(
- n: HasNetwork,
- tmt: Long = defaultTimeoutMs,
- valid: (NetworkCapabilities) -> Boolean
- ) = expectCapabilitiesThat(n.network, tmt, valid)
-
- @JvmOverloads
- fun expectCapabilitiesWith(
- capability: Int,
- n: HasNetwork,
- timeoutMs: Long = defaultTimeoutMs
- ): NetworkCapabilities {
- return expectCapabilitiesThat(n.network, timeoutMs) { it.hasCapability(capability) }.caps
- }
-
- @JvmOverloads
- fun expectCapabilitiesWithout(
- capability: Int,
- n: HasNetwork,
- timeoutMs: Long = defaultTimeoutMs
- ): NetworkCapabilities {
- return expectCapabilitiesThat(n.network, timeoutMs) { !it.hasCapability(capability) }.caps
- }
-
- fun expectBlockedStatusCallback(expectBlocked: Boolean, n: HasNetwork) {
- expectBlockedStatusCallback(expectBlocked, n.network, defaultTimeoutMs)
- }
-}
diff --git a/tests/lib/src/com/android/testutils/TestableNetworkStatsProvider.kt b/tests/lib/src/com/android/testutils/TestableNetworkStatsProvider.kt
deleted file mode 100644
index a4ef770..0000000
--- a/tests/lib/src/com/android/testutils/TestableNetworkStatsProvider.kt
+++ /dev/null
@@ -1,97 +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.testutils
-
-import android.net.netstats.provider.NetworkStatsProvider
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 200L
-
-open class TestableNetworkStatsProvider(
- val defaultTimeoutMs: Long = DEFAULT_TIMEOUT_MS
-) : NetworkStatsProvider() {
- sealed class CallbackType {
- data class OnRequestStatsUpdate(val token: Int) : CallbackType()
- data class OnSetLimit(val iface: String?, val quotaBytes: Long) : CallbackType()
- data class OnSetAlert(val quotaBytes: Long) : CallbackType()
- }
-
- val history = ArrayTrackRecord<CallbackType>().newReadHead()
- // See ReadHead#mark
- val mark get() = history.mark
-
- override fun onRequestStatsUpdate(token: Int) {
- history.add(CallbackType.OnRequestStatsUpdate(token))
- }
-
- override fun onSetLimit(iface: String, quotaBytes: Long) {
- history.add(CallbackType.OnSetLimit(iface, quotaBytes))
- }
-
- override fun onSetAlert(quotaBytes: Long) {
- history.add(CallbackType.OnSetAlert(quotaBytes))
- }
-
- fun expectOnRequestStatsUpdate(token: Int, timeout: Long = defaultTimeoutMs) {
- assertEquals(CallbackType.OnRequestStatsUpdate(token), history.poll(timeout))
- }
-
- fun expectOnSetLimit(iface: String?, quotaBytes: Long, timeout: Long = defaultTimeoutMs) {
- assertEquals(CallbackType.OnSetLimit(iface, quotaBytes), history.poll(timeout))
- }
-
- fun expectOnSetAlert(quotaBytes: Long, timeout: Long = defaultTimeoutMs) {
- assertEquals(CallbackType.OnSetAlert(quotaBytes), history.poll(timeout))
- }
-
- fun pollForNextCallback(timeout: Long = defaultTimeoutMs) =
- history.poll(timeout) ?: fail("Did not receive callback after ${timeout}ms")
-
- inline fun <reified T : CallbackType> expectCallback(
- timeout: Long = defaultTimeoutMs,
- predicate: (T) -> Boolean = { true }
- ): T {
- return pollForNextCallback(timeout).also { assertTrue(it is T && predicate(it)) } as T
- }
-
- // Expects a callback of the specified type matching the predicate within the timeout.
- // Any callback that doesn't match the predicate will be skipped. Fails only if
- // no matching callback is received within the timeout.
- // TODO : factorize the code for this with the identical call in TestableNetworkCallback.
- // There should be a common superclass doing this generically.
- // TODO : have a better error message to have this fail. Right now the failure when no
- // matching callback arrives comes from the casting to a non-nullable T.
- // TODO : in fact, completely removing this method and have clients use
- // history.poll(timeout, index, predicate) directly might be simpler.
- inline fun <reified T : CallbackType> eventuallyExpect(
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- crossinline predicate: (T) -> Boolean = { true }
- ) = history.poll(timeoutMs, from) { it is T && predicate(it) } as T
-
- fun drainCallbacks() {
- history.mark = history.size
- }
-
- @JvmOverloads
- fun assertNoCallback(timeout: Long = defaultTimeoutMs) {
- val cb = history.poll(timeout)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderBinder.kt b/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderBinder.kt
deleted file mode 100644
index 4d9f884..0000000
--- a/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderBinder.kt
+++ /dev/null
@@ -1,63 +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.testutils
-
-import android.net.netstats.provider.INetworkStatsProvider
-import kotlin.test.assertEquals
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 200L
-
-open class TestableNetworkStatsProviderBinder : INetworkStatsProvider.Stub() {
- sealed class CallbackType {
- data class OnRequestStatsUpdate(val token: Int) : CallbackType()
- data class OnSetLimit(val iface: String?, val quotaBytes: Long) : CallbackType()
- data class OnSetAlert(val quotaBytes: Long) : CallbackType()
- }
-
- private val history = ArrayTrackRecord<CallbackType>().ReadHead()
-
- override fun onRequestStatsUpdate(token: Int) {
- history.add(CallbackType.OnRequestStatsUpdate(token))
- }
-
- override fun onSetLimit(iface: String?, quotaBytes: Long) {
- history.add(CallbackType.OnSetLimit(iface, quotaBytes))
- }
-
- override fun onSetAlert(quotaBytes: Long) {
- history.add(CallbackType.OnSetAlert(quotaBytes))
- }
-
- fun expectOnRequestStatsUpdate(token: Int) {
- assertEquals(CallbackType.OnRequestStatsUpdate(token), history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- fun expectOnSetLimit(iface: String?, quotaBytes: Long) {
- assertEquals(CallbackType.OnSetLimit(iface, quotaBytes), history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- fun expectOnSetAlert(quotaBytes: Long) {
- assertEquals(CallbackType.OnSetAlert(quotaBytes), history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- @JvmOverloads
- fun assertNoCallback(timeout: Long = DEFAULT_TIMEOUT_MS) {
- val cb = history.poll(timeout)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt b/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt
deleted file mode 100644
index abce700..0000000
--- a/tests/lib/src/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt
+++ /dev/null
@@ -1,83 +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.testutils
-
-import android.net.NetworkStats
-import android.net.netstats.provider.INetworkStatsProviderCallback
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 3000L
-
-open class TestableNetworkStatsProviderCbBinder : INetworkStatsProviderCallback.Stub() {
- sealed class CallbackType {
- data class NotifyStatsUpdated(
- val token: Int,
- val ifaceStats: NetworkStats,
- val uidStats: NetworkStats
- ) : CallbackType()
- object NotifyLimitReached : CallbackType()
- object NotifyAlertReached : CallbackType()
- object Unregister : CallbackType()
- }
-
- private val history = ArrayTrackRecord<CallbackType>().ReadHead()
-
- override fun notifyStatsUpdated(token: Int, ifaceStats: NetworkStats, uidStats: NetworkStats) {
- history.add(CallbackType.NotifyStatsUpdated(token, ifaceStats, uidStats))
- }
-
- override fun notifyLimitReached() {
- history.add(CallbackType.NotifyLimitReached)
- }
-
- override fun notifyAlertReached() {
- history.add(CallbackType.NotifyAlertReached)
- }
-
- override fun unregister() {
- history.add(CallbackType.Unregister)
- }
-
- fun expectNotifyStatsUpdated() {
- val event = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(event is CallbackType.NotifyStatsUpdated)
- }
-
- fun expectNotifyStatsUpdated(ifaceStats: NetworkStats, uidStats: NetworkStats) {
- val event = history.poll(DEFAULT_TIMEOUT_MS)!!
- if (event !is CallbackType.NotifyStatsUpdated) {
- throw Exception("Expected NotifyStatsUpdated callback, but got ${event::class}")
- }
- // TODO: verify token.
- assertNetworkStatsEquals(ifaceStats, event.ifaceStats)
- assertNetworkStatsEquals(uidStats, event.uidStats)
- }
-
- fun expectNotifyLimitReached() =
- assertEquals(CallbackType.NotifyLimitReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- fun expectNotifyAlertReached() =
- assertEquals(CallbackType.NotifyAlertReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- // Assert there is no callback in current queue.
- fun assertNoCallback() {
- val cb = history.poll(0)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
index 9d2a630..ff03a8d 100644
--- a/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
@@ -85,6 +85,7 @@
// doesn't use == instead of equals when comparing addresses.
private static final Inet4Address ANY = v4Address("0.0.0.0");
private static final byte[] TEST_EMPTY_OPTIONS_SKIP_LIST = new byte[0];
+ private static final int TEST_IPV6_ONLY_WAIT_S = 1800; // 30 min
private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
@@ -484,6 +485,53 @@
assertTrue(dhcpResults.captivePortalApiUrl.length() > 0);
}
+ private void runIPv6OnlyPreferredOption(boolean enabled) throws Exception {
+ // CHECKSTYLE:OFF Generated code
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
+ // IP header.
+ "45100158000040004011B5CEC0A80164C0A80102" +
+ // UDP header
+ "004300440144CE63" +
+ // BOOTP header
+ "02010600B8BF41E60000000000000000C0A80102C0A8016400000000" +
+ // MAC address.
+ "22B3614EE01200000000000000000000" +
+ // Server name and padding.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options
+ "638253633501023604C0A80164330400000E103A04000007083B0400000C4E01" +
+ "04FFFFFF001C04C0A801FF0304C0A801640604C0A801640C0C74657374686F73" +
+ "746E616D651A0205DC" +
+ // Option 108 (0x6c, IPv6-Only preferred option), length 4 (0x04), 1800s
+ "6C0400000708" +
+ // End of options.
+ "FF"));
+ // CHECKSTYLE:ON Generated code
+
+ final DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3,
+ enabled ? TEST_EMPTY_OPTIONS_SKIP_LIST
+ : new byte[] { DhcpPacket.DHCP_IPV6_ONLY_PREFERRED });
+ assertTrue(offerPacket instanceof DhcpOfferPacket);
+ assertEquals(offerPacket.mIpv6OnlyWaitTime,
+ enabled ? new Integer(TEST_IPV6_ONLY_WAIT_S) : null);
+ }
+
+ @Test
+ public void testIPv6OnlyPreferredOption() throws Exception {
+ runIPv6OnlyPreferredOption(true /* enabled */);
+ }
+
+ @Test
+ public void testIPv6OnlyPreferredOption_Disable() throws Exception {
+ runIPv6OnlyPreferredOption(false /* enabled */);
+ }
+
@Test
public void testBadIpPacket() throws Exception {
final byte[] packet = HexDump.hexStringToByteArray(
diff --git a/tests/unit/src/android/net/dhcp/DhcpResultsParcelableUtilTest.java b/tests/unit/src/android/net/dhcp/DhcpResultsParcelableUtilTest.java
index 7d899e0..9ec1271 100644
--- a/tests/unit/src/android/net/dhcp/DhcpResultsParcelableUtilTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpResultsParcelableUtilTest.java
@@ -20,7 +20,7 @@
import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable;
-import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static org.junit.Assert.assertEquals;
diff --git a/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
index d313b94..58f9a36 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServerTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
@@ -53,12 +53,13 @@
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
import android.net.util.SharedLog;
+import android.os.ConditionVariable;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.net.module.util.Inet4AddressUtils;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.HandlerUtils;
import org.junit.After;
import org.junit.Before;
@@ -130,11 +131,30 @@
private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
@NonNull
- private DhcpServer mServer;
+ private MyDhcpServer mServer;
@Nullable
private String mPrevShareClassloaderProp;
+ private class MyDhcpServer extends DhcpServer {
+ private final ConditionVariable mCv = new ConditionVariable(false);
+
+ MyDhcpServer(Context context, String ifName, DhcpServingParams params, SharedLog log,
+ Dependencies deps) {
+ super(context, ifName, params, log, deps);
+ }
+
+ @Override
+ protected void onQuitting() {
+ super.onQuitting();
+ mCv.open();
+ }
+
+ public void waitForShutdown() {
+ assertTrue(mCv.block(TEST_TIMEOUT_MS));
+ }
+ }
+
private final INetworkStackStatusCallback mAssertSuccessCallback =
new INetworkStackStatusCallback.Stub() {
@Override
@@ -167,7 +187,7 @@
private void startServer() throws Exception {
mServer.start(mAssertSuccessCallback);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
}
@Before
@@ -183,7 +203,7 @@
when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME);
when(mPacketListener.start()).thenReturn(true);
- mServer = new DhcpServer(mContext, TEST_IFACE, makeServingParams(),
+ mServer = new MyDhcpServer(mContext, TEST_IFACE, makeServingParams(),
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
}
@@ -191,7 +211,7 @@
public void tearDown() throws Exception {
verify(mRepository, never()).addLeaseCallbacks(eq(null));
mServer.stop(mAssertSuccessCallback);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ mServer.waitForShutdown();
verify(mPacketListener, times(1)).stop();
}
@@ -205,7 +225,7 @@
@Test
public void testStartWithCallbacks() throws Exception {
mServer.start(mAssertSuccessCallback, mEventCallbacks);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
}
@@ -222,7 +242,7 @@
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpOfferPacket packet = assertOffer(getPacket());
@@ -241,7 +261,7 @@
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, true /* rapidCommit */);
mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -260,7 +280,7 @@
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -289,7 +309,7 @@
request.mHostName = TEST_HOSTNAME;
request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
mServer.sendMessage(CMD_RECEIVE_PACKET, request);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -307,7 +327,7 @@
final DhcpRequestPacket request = makeRequestSelectingPacket();
mServer.sendMessage(CMD_RECEIVE_PACKET, request);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -322,7 +342,7 @@
TEST_SERVER_ADDR, TEST_CLIENT_ADDR,
INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES);
mServer.sendMessage(CMD_RECEIVE_PACKET, release);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository, times(1))
.releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR));
@@ -348,7 +368,7 @@
INADDR_ANY /* nextIp */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
TEST_CLIENT_ADDR /* requestedIp */, TEST_SERVER_ADDR /* serverIdentifier */);
mServer.sendMessage(CMD_RECEIVE_PACKET, decline);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository).markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
eq(TEST_CLIENT_ADDR));
@@ -373,7 +393,7 @@
params.changePrefixOnDecline = changePrefixOnDecline;
mServer.updateParams(params, mAssertSuccessCallback);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
}
@Test
@@ -382,7 +402,7 @@
eq(TEST_CLIENT_ADDR))).thenReturn(true);
mServer.start(mAssertSuccessCallback, mEventCallbacks);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
// Enable changePrefixOnDecline
@@ -416,7 +436,7 @@
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
- HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(clientAddr);
final DhcpOfferPacket packet = assertOffer(getPacket());
assertMatchesLease(packet, serverAddr, clientAddr, null);
diff --git a/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java b/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
index 6506eba..290fd69 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -34,7 +34,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.Inet4AddressUtils;
-import com.android.testutils.MiscAssertsKt;
+import com.android.testutils.MiscAsserts;
import org.junit.Before;
import org.junit.Test;
@@ -199,7 +199,7 @@
assertEquals(params.singleClientAddr, parceled.singleClientAddr);
assertEquals(params.changePrefixOnDecline, parceled.changePrefixOnDecline);
- MiscAssertsKt.assertFieldCountEquals(10, DhcpServingParamsParcel.class);
+ MiscAsserts.assertFieldCountEquals(10, DhcpServingParamsParcel.class);
}
@Test(expected = InvalidParameterException.class)
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java
index e2b8e4f..e991ea7 100644
--- a/tests/unit/src/android/net/ip/IpClientTest.java
+++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -61,7 +61,7 @@
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.HandlerUtils;
import org.junit.Before;
import org.junit.Test;
@@ -262,7 +262,7 @@
lp.getDnsServers().stream().map(InetAddress::getHostAddress)
.toArray(String[]::new));
- HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
verify(mCb, never()).onProvisioningFailure(any());
verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());
@@ -301,7 +301,7 @@
reset(mCb);
doIPv6ProvisioningLoss(lp);
- HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
verify(mCb).onProvisioningFailure(lp);
verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME));
@@ -326,11 +326,11 @@
final IpClient ipc = doProvisioningWithDefaultConfiguration();
final LinkProperties lp = makeIPv6ProvisionedLinkProperties();
addIPv4Provisioning(lp);
- HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
reset(mCb);
doIPv6ProvisioningLoss(lp);
- HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
if (avoidBadWifi) { // Provisioning failure is expected only when avoidBadWifi is true
verify(mCb).onProvisioningFailure(lp);
verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME));
diff --git a/tests/unit/src/android/net/netlink/NetlinkTestUtils.kt b/tests/unit/src/android/net/netlink/NetlinkTestUtils.kt
index 1bc3ab0..3b485eb 100644
--- a/tests/unit/src/android/net/netlink/NetlinkTestUtils.kt
+++ b/tests/unit/src/android/net/netlink/NetlinkTestUtils.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:JvmName("NetlinkTestUtils")
+
package android.net.netlink
import android.net.netlink.NetlinkConstants.RTM_DELNEIGH
diff --git a/tests/unit/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/tests/unit/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
index 34257b8..ca9ae80 100644
--- a/tests/unit/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/tests/unit/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -16,8 +16,8 @@
package android.net.netlink;
-import static android.net.netlink.NetlinkTestUtilsKt.makeDelNeighMessage;
-import static android.net.netlink.NetlinkTestUtilsKt.makeNewNeighMessage;
+import static android.net.netlink.NetlinkTestUtils.makeDelNeighMessage;
+import static android.net.netlink.NetlinkTestUtils.makeNewNeighMessage;
import static android.net.netlink.StructNdMsg.NUD_STALE;
import static org.junit.Assert.assertEquals;
diff --git a/tests/unit/src/android/net/netlink/StructNdOptPref64Test.java b/tests/unit/src/android/net/netlink/StructNdOptPref64Test.java
index a09805e..0f3020d 100644
--- a/tests/unit/src/android/net/netlink/StructNdOptPref64Test.java
+++ b/tests/unit/src/android/net/netlink/StructNdOptPref64Test.java
@@ -20,7 +20,7 @@
import static android.net.netlink.StructNdOptPref64.plcToPrefixLength;
import static android.net.netlink.StructNdOptPref64.prefixLengthToPlc;
-import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
diff --git a/tests/unit/src/android/net/shared/InitialConfigurationTest.java b/tests/unit/src/android/net/shared/InitialConfigurationTest.java
index 17f8324..4d5a7df 100644
--- a/tests/unit/src/android/net/shared/InitialConfigurationTest.java
+++ b/tests/unit/src/android/net/shared/InitialConfigurationTest.java
@@ -18,7 +18,7 @@
import static android.net.InetAddresses.parseNumericAddress;
-import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
diff --git a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
index 49cea7d..28e787b 100644
--- a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
+++ b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
@@ -19,7 +19,7 @@
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.shared.ProvisioningConfiguration.fromStableParcelable;
-import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
diff --git a/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt b/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt
index 4e4d25a..5b91985 100644
--- a/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt
+++ b/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt
@@ -4,7 +4,7 @@
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
-import com.android.testutils.ConcurrentIntepreter
+import com.android.testutils.ConcurrentInterpreter
import com.android.testutils.InterpretMatcher
import com.android.testutils.RecorderCallback.CallbackEntry
import com.android.testutils.RecorderCallback.CallbackEntry.Available
@@ -247,7 +247,7 @@
}
}
-private object TNCInterpreter : ConcurrentIntepreter<TestableNetworkCallback>(interpretTable)
+private object TNCInterpreter : ConcurrentInterpreter<TestableNetworkCallback>(interpretTable)
val EntryList = CallbackEntry::class.sealedSubclasses.map { it.simpleName }.joinToString("|")
private fun callbackEntryFromString(name: String): KClass<out CallbackEntry> {
diff --git a/tests/unit/src/android/net/util/NetworkStackUtilsTest.java b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java
index 80b4272..93ea32f 100644
--- a/tests/unit/src/android/net/util/NetworkStackUtilsTest.java
+++ b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java
@@ -34,11 +34,14 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.networkstack.R;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +72,7 @@
@Mock private Context mContext;
@Mock private PackageManager mPm;
@Mock private PackageInfo mPi;
+ @Mock private Resources mResources;
@Before
public void setUp() throws Exception {
@@ -81,6 +85,7 @@
doReturn(mPm).when(mContext).getPackageManager();
doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
doReturn(pi).when(mPm).getPackageInfo(anyString(), anyInt());
+ doReturn(mResources).when(mContext).getResources();
}
@After
@@ -199,4 +204,18 @@
assertFalse(isIPv6ULA(parseNumericAddress("fe00::")));
assertFalse(isIPv6ULA(parseNumericAddress("2480:1248::123:456")));
}
+
+ @Test
+ public void testGetResBooleanConfig() {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
+ assertTrue(NetworkStackUtils.getResBooleanConfig(mContext,
+ R.bool.config_no_sim_card_uses_neighbor_mcc, false));
+ doReturn(false).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
+ assertFalse(NetworkStackUtils.getResBooleanConfig(mContext,
+ R.bool.config_no_sim_card_uses_neighbor_mcc, false));
+ doThrow(new Resources.NotFoundException())
+ .when(mResources).getBoolean(eq(R.bool.config_no_sim_card_uses_neighbor_mcc));
+ assertFalse(NetworkStackUtils.getResBooleanConfig(mContext,
+ R.bool.config_no_sim_card_uses_neighbor_mcc, false));
+ }
}
\ No newline at end of file
diff --git a/tests/unit/src/android/net/util/PacketReaderTest.java b/tests/unit/src/android/net/util/PacketReaderTest.java
deleted file mode 100644
index 3947d15..0000000
--- a/tests/unit/src/android/net/util/PacketReaderTest.java
+++ /dev/null
@@ -1,241 +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 android.net.util;
-
-import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_SNDTIMEO;
-
-import static com.android.testutils.MiscAssertsKt.assertThrows;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructTimeval;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileDescriptor;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for PacketReader.
- *
- * @hide
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PacketReaderTest {
- static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
- static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
-
- protected CountDownLatch mLatch;
- protected FileDescriptor mLocalSocket;
- protected InetSocketAddress mLocalSockName;
- protected byte[] mLastRecvBuf;
- protected boolean mStopped;
- protected HandlerThread mHandlerThread;
- protected PacketReader mReceiver;
-
- class UdpLoopbackReader extends PacketReader {
- public UdpLoopbackReader(Handler h) {
- super(h);
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
- Os.bind(s, LOOPBACK6, 0);
- mLocalSockName = (InetSocketAddress) Os.getsockname(s);
- Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
- } catch (ErrnoException|SocketException e) {
- closeFd(s);
- fail();
- return null;
- }
-
- mLocalSocket = s;
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- mLastRecvBuf = Arrays.copyOf(recvbuf, length);
- mLatch.countDown();
- }
-
- @Override
- protected void onStart() {
- mStopped = false;
- mLatch.countDown();
- }
-
- @Override
- protected void onStop() {
- mStopped = true;
- mLatch.countDown();
- }
- };
-
- @Before
- public void setUp() {
- resetLatch();
- mLocalSocket = null;
- mLocalSockName = null;
- mLastRecvBuf = null;
- mStopped = false;
-
- mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
- mHandlerThread.start();
- }
-
- @After
- public void tearDown() throws Exception {
- if (mReceiver != null) {
- mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
- waitForActivity();
- }
- mReceiver = null;
- mHandlerThread.quit();
- mHandlerThread = null;
- }
-
- void resetLatch() { mLatch = new CountDownLatch(1); }
-
- void waitForActivity() throws Exception {
- try {
- mLatch.await(1000, TimeUnit.MILLISECONDS);
- } finally {
- resetLatch();
- }
- }
-
- void sendPacket(byte[] contents) throws Exception {
- final DatagramSocket sender = new DatagramSocket();
- sender.connect(mLocalSockName);
- sender.send(new DatagramPacket(contents, contents.length));
- sender.close();
- }
-
- @Test
- public void testBasicWorking() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- mReceiver = new UdpLoopbackReader(h);
-
- h.post(() -> { mReceiver.start(); });
- waitForActivity();
- assertTrue(mLocalSockName != null);
- assertEquals(LOOPBACK6, mLocalSockName.getAddress());
- assertTrue(0 < mLocalSockName.getPort());
- assertTrue(mLocalSocket != null);
- assertFalse(mStopped);
-
- final byte[] one = "one 1".getBytes("UTF-8");
- sendPacket(one);
- waitForActivity();
- assertEquals(1, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(one, mLastRecvBuf));
- assertFalse(mStopped);
-
- final byte[] two = "two 2".getBytes("UTF-8");
- sendPacket(two);
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertFalse(mStopped);
-
- h.post(() -> mReceiver.stop());
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertTrue(mStopped);
- mReceiver = null;
- }
-
- class NullPacketReader extends PacketReader {
- public NullPacketReader(Handler h, int recvbufsize) {
- super(h, recvbufsize);
- }
-
- @Override
- public FileDescriptor createFd() { return null; }
- }
-
- @Test
- public void testMinimalRecvBufSize() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
-
- for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
- final PacketReader b = new NullPacketReader(h, i);
- assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
- }
- }
-
- @Test
- public void testStartingFromWrongThread() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE);
- assertThrows(IllegalStateException.class, () -> b.start());
- }
-
- @Test
- public void testStoppingFromWrongThread() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE);
- assertThrows(IllegalStateException.class, () -> b.stop());
- }
-
- @Test
- public void testSuccessToCreateSocket() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- final PacketReader b = new UdpLoopbackReader(h);
- h.post(() -> assertTrue(b.start()));
- }
-
- @Test
- public void testFailToCreateSocket() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- final PacketReader b = new NullPacketReader(h, DEFAULT_RECV_BUF_SIZE);
- h.post(() -> assertFalse(b.start()));
- }
-}
diff --git a/tests/unit/src/android/net/testutils/TrackRecordTest.kt b/tests/unit/src/com/android/net/module/util/TrackRecordTest.kt
similarity index 98%
rename from tests/unit/src/android/net/testutils/TrackRecordTest.kt
rename to tests/unit/src/com/android/net/module/util/TrackRecordTest.kt
index 995d537..c2bdcd4 100644
--- a/tests/unit/src/android/net/testutils/TrackRecordTest.kt
+++ b/tests/unit/src/com/android/net/module/util/TrackRecordTest.kt
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package android.net.testutils
+package com.android.net.module.util
-import com.android.testutils.ArrayTrackRecord
-import com.android.testutils.ConcurrentIntepreter
+import com.android.testutils.ConcurrentInterpreter
import com.android.testutils.InterpretException
import com.android.testutils.InterpretMatcher
import com.android.testutils.SyntaxException
-import com.android.testutils.TrackRecord
import com.android.testutils.__FILE__
import com.android.testutils.__LINE__
import com.android.testutils.intArg
@@ -350,7 +348,7 @@
}
}
-private object TRTInterpreter : ConcurrentIntepreter<TrackRecord<Int>>(interpretTable) {
+private object TRTInterpreter : ConcurrentInterpreter<TrackRecord<Int>>(interpretTable) {
fun interpretTestSpec(spec: String, useReadHeads: Boolean) = if (useReadHeads) {
interpretTestSpec(spec, initial = ArrayTrackRecord(),
threadTransform = { (it as ArrayTrackRecord).newReadHead() })
diff --git a/tests/unit/src/com/android/networkstack/arp/ArpPacketTest.java b/tests/unit/src/com/android/networkstack/arp/ArpPacketTest.java
index 18f4e04..12dc0fb 100644
--- a/tests/unit/src/com/android/networkstack/arp/ArpPacketTest.java
+++ b/tests/unit/src/com/android/networkstack/arp/ArpPacketTest.java
@@ -18,7 +18,7 @@
import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
-import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
index a884f7e..8fbe0c4 100644
--- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
+++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -207,7 +207,6 @@
private static final int TEST_NETID2_FWMARK = 0x1A85;
private static final int NETID_MASK = 0xffff;
@Mock private TcpSocketTracker.Dependencies mDependencies;
- @Mock private FileDescriptor mMockFd;
@Mock private INetd mNetd;
private final Network mNetwork = new Network(TEST_NETID1);
private final Network mOtherNetwork = new Network(TEST_NETID2);
@@ -226,7 +225,7 @@
Log.setWtfHandler((tag, what, system) -> Log.e(tag, what.getMessage(), what));
when(mDependencies.getNetd()).thenReturn(mNetd);
when(mDependencies.isTcpInfoParsingSupported()).thenReturn(true);
- when(mDependencies.connectToKernel()).thenReturn(mMockFd);
+ when(mDependencies.connectToKernel()).thenReturn(new FileDescriptor());
when(mDependencies.getDeviceConfigPropertyInt(
eq(NAMESPACE_CONNECTIVITY),
eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE),
diff --git a/tests/unit/src/com/android/server/NetworkStackServiceTest.kt b/tests/unit/src/com/android/server/NetworkStackServiceTest.kt
index c054b3a..df0787e 100644
--- a/tests/unit/src/com/android/server/NetworkStackServiceTest.kt
+++ b/tests/unit/src/com/android/server/NetworkStackServiceTest.kt
@@ -31,6 +31,7 @@
import android.net.ip.IpClient
import android.os.Build
import android.os.IBinder
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH
@@ -42,6 +43,8 @@
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.ExceptionUtils
+import com.android.testutils.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -202,8 +205,14 @@
connector.makeDhcpServer(TEST_IFACE, testParams, mockDhcpCb)
verify(mockDhcpCb, times(2)).onDhcpServerCreated(eq(IDhcpServer.STATUS_SUCCESS), any())
- // Verify all methods were covered by the test (4 methods + getVersion + getHash)
- assertEquals(6, INetworkStackConnector::class.declaredMemberFunctions.count {
+ // allowTestUid does not need to record the caller's version
+ assertThrows(SecurityException::class.java, ExceptionUtils.ThrowingRunnable {
+ // Should throw because the test does not run as root
+ connector.allowTestUid(Process.myUid(), null)
+ })
+
+ // Verify all methods were covered by the test (5 methods + getVersion + getHash)
+ assertEquals(7, INetworkStackConnector::class.declaredMemberFunctions.count {
it.visibility == KVisibility.PUBLIC
})
}
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index a3ef532..0d3a51a 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -152,7 +152,7 @@
import com.android.server.connectivity.nano.WifiData;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.HandlerUtils;
import com.google.protobuf.nano.MessageNano;
@@ -631,7 +631,7 @@
final WrappedNetworkMonitor nm = new WrappedNetworkMonitor();
nm.start();
setNetworkCapabilities(nm, nc);
- HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
mCreatedNetworkMonitors.add(nm);
when(mTstDependencies.isTcpInfoParsingSupported()).thenReturn(false);
@@ -655,12 +655,13 @@
private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
nm.notifyNetworkCapabilitiesChanged(nc);
- HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
}
@Test
public void testOnlyWifiTransport() {
- final WrappedNetworkMonitor wnm = makeCellMeteredNetworkMonitor();
+ final WrappedNetworkMonitor wnm = makeMonitor(CELL_METERED_CAPABILITIES);
+ assertFalse(wnm.onlyWifiTransport());
final NetworkCapabilities nc = new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
.addTransportType(TRANSPORT_VPN);
@@ -926,6 +927,18 @@
return info;
}
+ private void setupNoSimCardNeighborMcc() throws Exception {
+ // Enable using neighbor resource by camping mcc feature.
+ doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
+ final List<CellInfo> cellList = new ArrayList<CellInfo>();
+ final int testMcc = 460;
+ cellList.add(makeTestCellInfoGsm(Integer.toString(testMcc)));
+ doReturn(cellList).when(mTelephony).getAllCellInfo();
+ final Configuration config = mResources.getConfiguration();
+ config.mcc = testMcc;
+ doReturn(mMccContext).when(mContext).createConfigurationContext(eq(config));
+ }
+
@Test
public void testMakeFallbackUrls() throws Exception {
final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
@@ -949,21 +962,28 @@
assertEquals("http://testUrl1.com", urls[0].toString());
assertEquals("http://testUrl2.com", urls[1].toString());
- // Value is expected to be replaced by location resource.
- doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc);
-
- final List<CellInfo> cellList = new ArrayList<CellInfo>();
- final int testMcc = 460;
- cellList.add(makeTestCellInfoGsm(Integer.toString(testMcc)));
- doReturn(cellList).when(mTelephony).getAllCellInfo();
- final Configuration config = mResources.getConfiguration();
- config.mcc = testMcc;
- doReturn(mMccContext).when(mContext).createConfigurationContext(eq(config));
+ // Even though the using neighbor resource by camping mcc feature is enabled, the
+ // customized context has been assigned and won't change. So calling
+ // makeCaptivePortalFallbackUrls() still gets the original value.
+ setupNoSimCardNeighborMcc();
doReturn(new String[] {"http://testUrl3.com"}).when(mMccResource)
.getStringArray(R.array.config_captive_portal_fallback_urls);
urls = wnm.makeCaptivePortalFallbackUrls();
+ assertEquals(urls.length, 2);
+ assertEquals("http://testUrl1.com", urls[0].toString());
+ assertEquals("http://testUrl2.com", urls[1].toString());
+ }
+
+ @Test
+ public void testMakeFallbackUrlsWithCustomizedContext() throws Exception {
+ // Value is expected to be replaced by location resource.
+ setupNoSimCardNeighborMcc();
+ doReturn(new String[] {"http://testUrl.com"}).when(mMccResource)
+ .getStringArray(R.array.config_captive_portal_fallback_urls);
+ final WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor();
+ final URL[] urls = wnm.makeCaptivePortalFallbackUrls();
assertEquals(urls.length, 1);
- assertEquals("http://testUrl3.com", urls[0].toString());
+ assertEquals("http://testUrl.com", urls[0].toString());
}
private static CellIdentityGsm makeCellIdentityGsm(int lac, int cid, int arfcn, int bsic,
@@ -1120,7 +1140,8 @@
// Second check should be triggered automatically after the reevaluate delay, and uses the
// URL chosen by mRandom
- // This test is appropriate to cover reevaluate behavior as long as the timeout is short
+ // Ensure that the reevaluate delay is not changed to a large value, otherwise this test
+ // would block for too long and a different test strategy should be used.
assertTrue(INITIAL_REEVALUATE_DELAY_MS < 2000);
verify(mOtherFallbackConnection, timeout(INITIAL_REEVALUATE_DELAY_MS + HANDLER_TIMEOUT_MS))
.getResponseCode();
@@ -1520,6 +1541,32 @@
}
@Test
+ public void testIsCaptivePortal_TestUrlsWithUrlOverlays() throws Exception {
+ setupResourceForMultipleProbes();
+ doReturn(TEST_HTTPS_URL).when(mResources)
+ .getString(R.string.config_captive_portal_https_url);
+ doReturn(TEST_HTTP_URL).when(mResources)
+ .getString(R.string.config_captive_portal_http_url);
+
+ setDeviceConfig(TEST_URL_EXPIRATION_TIME,
+ String.valueOf(currentTimeMillis() + TimeUnit.MINUTES.toMillis(9)));
+ setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL, TEST_OVERRIDE_URL);
+ setDeviceConfig(TEST_CAPTIVE_PORTAL_HTTP_URL, TEST_OVERRIDE_URL);
+ setStatus(mTestOverriddenUrlConnection, 204);
+
+ runValidatedNetworkTest();
+ verify(mHttpsConnection, never()).getResponseCode();
+ verify(mHttpConnection, never()).getResponseCode();
+ verify(mOtherHttpsConnection1, never()).getResponseCode();
+ verify(mOtherHttpsConnection2, never()).getResponseCode();
+ verify(mOtherHttpConnection1, never()).getResponseCode();
+ verify(mOtherHttpConnection2, never()).getResponseCode();
+
+ // Used for both HTTP and HTTPS: can be called once (if HTTPS validates first) or twice
+ verify(mTestOverriddenUrlConnection, atLeastOnce()).getResponseCode();
+ }
+
+ @Test
public void testIsDataStall_EvaluationDisabled() {
setDataStallEvaluationType(0);
WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor();
@@ -1630,7 +1677,7 @@
// Trigger a tcp event immediately.
setTcpPollingInterval(0);
wrappedMonitor.sendTcpPollingEvent();
- HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
assertFalse(wrappedMonitor.isDataStall());
when(mTst.getLatestReceivedCount()).thenReturn(0);
@@ -1638,19 +1685,38 @@
// Trigger a tcp event immediately.
setTcpPollingInterval(0);
wrappedMonitor.sendTcpPollingEvent();
- HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
assertTrue(wrappedMonitor.isDataStall());
verify(mCallbacks).notifyDataStallSuspected(matchTcpDataStallParcelable());
}
@Test
+ public void testIsDataStall_EvaluationDnsAndTcp() throws Exception {
+ setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP);
+ setupTcpDataStall();
+ final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES);
+ nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+ assertTrue(nm.isDataStall());
+ verify(mCallbacks).notifyDataStallSuspected(
+ matchDnsAndTcpDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD));
+
+ when(mTst.getLatestReceivedCount()).thenReturn(5);
+ // Trigger a tcp event immediately.
+ setTcpPollingInterval(0);
+ nm.sendTcpPollingEvent();
+ HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS);
+ assertFalse(nm.isDataStall());
+ }
+
+ @Test
public void testIsDataStall_DisableTcp() {
// Disable tcp detection with only DNS detect. keep the tcp signal but set to no DNS signal.
setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
WrappedNetworkMonitor wrappedMonitor = makeMonitor(CELL_METERED_CAPABILITIES);
makeDnsSuccessEvent(wrappedMonitor, 1);
wrappedMonitor.sendTcpPollingEvent();
- HandlerUtilsKt.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(wrappedMonitor.getHandler(), HANDLER_TIMEOUT_MS);
assertFalse(wrappedMonitor.isDataStall());
verify(mTst, never()).isDataStallSuspected();
verify(mTst, never()).pollSocketsInfo();
@@ -1773,7 +1839,7 @@
wnm.forceReevaluation(Process.myUid());
// ProbeCompleted should be reset to 0
- HandlerUtilsKt.waitForIdle(wnm.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(wnm.getHandler(), HANDLER_TIMEOUT_MS);
assertEquals(wnm.getEvaluationState().getProbeCompletedResult(), 0);
verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, PROBES_PRIVDNS_VALID);
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).notifyProbeStatusChanged(
@@ -1879,28 +1945,28 @@
@Test
public void testDataStall_StallTcpSuspectedAndSendMetricsOnCell() throws Exception {
- testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_CELLULAR,
- CELL_METERED_CAPABILITIES);
+ testDataStall_StallTcpSuspectedAndSendMetrics(CELL_METERED_CAPABILITIES);
}
@Test
public void testDataStall_StallTcpSuspectedAndSendMetricsOnWifi() throws Exception {
- testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities.TRANSPORT_WIFI,
- WIFI_NOT_METERED_CAPABILITIES);
+ testDataStall_StallTcpSuspectedAndSendMetrics(WIFI_NOT_METERED_CAPABILITIES);
}
- private void testDataStall_StallTcpSuspectedAndSendMetrics(int transport,
- NetworkCapabilities nc) throws Exception {
+ private void testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities nc)
+ throws Exception {
assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
setupTcpDataStall();
+ setTcpPollingInterval(0);
// NM suspects data stall from TCP signal and sends data stall metrics.
setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP);
final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc);
-
// Trigger a tcp event immediately.
- setTcpPollingInterval(0);
nm.sendTcpPollingEvent();
- verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transport);
+ // Allow only one transport type in the context of this test for simplification.
+ final int[] transports = nc.getTransportTypes();
+ assertEquals(1, transports.length);
+ verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transports[0]);
}
private WrappedNetworkMonitor prepareNetworkMonitorForVerifyDataStall(NetworkCapabilities nc)
@@ -1909,20 +1975,15 @@
// evaluation will only start from validated state.
setStatus(mHttpsConnection, 204);
final WrappedNetworkMonitor nm;
- final int[] transports = nc.getTransportTypes();
- // Though multiple transport types are allowed, use the first transport type for
- // simplification.
- switch (transports[0]) {
- case NetworkCapabilities.TRANSPORT_CELLULAR:
- nm = makeCellMeteredNetworkMonitor();
- break;
- case NetworkCapabilities.TRANSPORT_WIFI:
- nm = makeWifiNotMeteredNetworkMonitor();
- setupTestWifiInfo();
- break;
- default:
- nm = null;
- fail("Undefined transport type");
+ // Allow only one transport type in the context of this test for simplification.
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ nm = makeCellMeteredNetworkMonitor();
+ } else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ nm = makeWifiNotMeteredNetworkMonitor();
+ setupTestWifiInfo();
+ } else {
+ nm = null;
+ fail("Undefined transport type");
}
nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID,
@@ -2510,8 +2571,8 @@
}
private void setTcpPollingInterval(int time) {
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(CONFIG_DATA_STALL_TCP_POLLING_INTERVAL), anyInt())).thenReturn(time);
+ doReturn(time).when(mDependencies).getDeviceConfigPropertyInt(any(),
+ eq(CONFIG_DATA_STALL_TCP_POLLING_INTERVAL), anyInt());
}
private void setFallbackUrl(String url) {
@@ -2591,7 +2652,7 @@
final NetworkMonitor monitor = makeMonitor(nc);
monitor.notifyNetworkConnected(lp, nc);
verifyNetworkTested(testResult, probesSucceeded, redirectUrl);
- HandlerUtilsKt.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS);
+ HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS);
return monitor;
}
@@ -2686,13 +2747,20 @@
&& Objects.equals(p.redirectUrl, redirectUrl));
}
+ private DataStallReportParcelable matchDnsAndTcpDataStallParcelable(final int timeoutCount) {
+ return argThat(p ->
+ (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0
+ && (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0
+ && p.dnsConsecutiveTimeouts == timeoutCount);
+ }
+
private DataStallReportParcelable matchDnsDataStallParcelable(final int timeoutCount) {
- return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_DNS_EVENTS
+ return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_DNS_EVENTS) != 0
&& p.dnsConsecutiveTimeouts == timeoutCount);
}
private DataStallReportParcelable matchTcpDataStallParcelable() {
- return argThat(p -> p.detectionMethod == ConstantsShim.DETECTION_METHOD_TCP_METRICS);
+ return argThat(p -> (p.detectionMethod & ConstantsShim.DETECTION_METHOD_TCP_METRICS) != 0);
}
}