Merge "Ignore DnsResolverTapTest in instant mode" into main
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index ce144a7..39bf1f7 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -1475,6 +1475,13 @@
return 1;
}
+ // W bumps the kernel requirement up to 5.4
+ // see also: //system/netd/tests/kernel_test.cpp TestKernel54
+ if (isAtLeastW && !isAtLeastKernelVersion(5, 4, 0)) {
+ ALOGE("Android W requires kernel 5.4.");
+ return 1;
+ }
+
// Technically already required by U, but only enforce on V+
// see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16, 0)) {
@@ -1498,13 +1505,13 @@
bool bad = false;
if (!isLtsKernel()) {
- ALOGW("Android V only supports LTS kernels.");
+ ALOGW("Android V+ only supports LTS kernels.");
bad = true;
}
#define REQUIRE(maj, min, sub) \
if (isKernelVersion(maj, min) && !isAtLeastKernelVersion(maj, min, sub)) { \
- ALOGW("Android V requires %d.%d kernel to be %d.%d.%d+.", maj, min, maj, min, sub); \
+ ALOGW("Android V+ requires %d.%d kernel to be %d.%d.%d+.", maj, min, maj, min, sub); \
bad = true; \
}
diff --git a/bpf/netd/BpfHandler.cpp b/bpf/netd/BpfHandler.cpp
index 340acda..bcd0cba 100644
--- a/bpf/netd/BpfHandler.cpp
+++ b/bpf/netd/BpfHandler.cpp
@@ -70,6 +70,13 @@
return netdutils::status::ok;
}
+// Checks if the device is running on release version of Android 25Q2 or newer.
+static bool isAtLeast25Q2() {
+ return android_get_device_api_level() >= 36 ||
+ (android_get_device_api_level() == 35 &&
+ modules::sdklevel::detail::IsAtLeastPreReleaseCodename("Baklava"));
+}
+
static Status initPrograms(const char* cg2_path) {
if (!cg2_path) return Status("cg2_path is NULL");
@@ -91,6 +98,16 @@
return Status("U+ platform with cg2_path != /sys/fs/cgroup is unsupported");
}
+ // V bumps the kernel requirement up to 4.19
+ if (modules::sdklevel::IsAtLeastV() && !bpf::isAtLeastKernelVersion(4, 19, 0)) {
+ return Status("V+ platform with kernel version < 4.19.0 is unsupported");
+ }
+
+ // 25Q2 bumps the kernel requirement up to 5.4
+ if (isAtLeast25Q2() && !bpf::isAtLeastKernelVersion(5, 4, 0)) {
+ return Status("25Q2+ platform with kernel version < 5.4.0 is unsupported");
+ }
+
unique_fd cg_fd(open(cg2_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
if (!cg_fd.ok()) {
const int err = errno;
diff --git a/framework/api/current.txt b/framework/api/current.txt
index a9d1569..323c533 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -244,7 +244,7 @@
field public static final int HEADER_COMPRESSION_6LOWPAN = 2; // 0x2
field public static final int HEADER_COMPRESSION_ANY = 0; // 0x0
field public static final int HEADER_COMPRESSION_NONE = 1; // 0x1
- field public static final int PSM_ANY = -1; // 0xffffffff
+ field public static final int PSM_ANY = 0; // 0x0
field public static final int ROLE_ANY = 0; // 0x0
field public static final int ROLE_CLIENT = 1; // 0x1
field public static final int ROLE_SERVER = 2; // 0x2
@@ -254,8 +254,8 @@
ctor public L2capNetworkSpecifier.Builder();
method @NonNull public android.net.L2capNetworkSpecifier build();
method @NonNull public android.net.L2capNetworkSpecifier.Builder setHeaderCompression(int);
- method @NonNull public android.net.L2capNetworkSpecifier.Builder setPsm(int);
- method @NonNull public android.net.L2capNetworkSpecifier.Builder setRemoteAddress(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.L2capNetworkSpecifier.Builder setPsm(@IntRange(from=0, to=255) int);
+ method @NonNull public android.net.L2capNetworkSpecifier.Builder setRemoteAddress(@Nullable android.net.MacAddress);
method @NonNull public android.net.L2capNetworkSpecifier.Builder setRole(int);
}
diff --git a/framework/src/android/net/L2capNetworkSpecifier.java b/framework/src/android/net/L2capNetworkSpecifier.java
index c7067f6..3c95dd0 100644
--- a/framework/src/android/net/L2capNetworkSpecifier.java
+++ b/framework/src/android/net/L2capNetworkSpecifier.java
@@ -18,8 +18,10 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,34 +32,46 @@
import java.util.Objects;
/**
- * A {@link NetworkSpecifier} used to identify an L2CAP network.
+ * A {@link NetworkSpecifier} used to identify an L2CAP network over BLE.
*
* An L2CAP network is not symmetrical, meaning there exists both a server (Bluetooth peripheral)
- * and a client (Bluetooth central) node. This specifier contains information required to request or
- * reserve an L2CAP network.
+ * and a client (Bluetooth central) node. This specifier contains the information required to
+ * request a client L2CAP network using {@link ConnectivityManager#requestNetwork} while specifying
+ * the remote MAC address, and Protocol/Service Multiplexer (PSM). It can also contain information
+ * allocated by the system when reserving a server network using {@link
+ * ConnectivityManager#reserveNetwork} such as the Protocol/Service Multiplexer (PSM). In both
+ * cases, the header compression option must be specified.
*
- * An L2CAP server network allocates a PSM to be advertised to the client. Therefore, the server
- * network must always be reserved using {@link ConnectivityManager#reserveNetwork}. The subsequent
- * {@link ConnectivityManager.NetworkCallback#onReserved(NetworkCapabilities)} includes information
- * (i.e. the PSM) for the server to advertise to the client.
- * Under the hood, an L2CAP server network is represented by a {@link
- * android.bluetooth.BluetoothServerSocket} which can, in theory, accept many connections. However,
- * before Android 15 Bluetooth APIs do not expose the channel ID, so these connections are
- * indistinguishable. In practice, this means that network matching semantics in {@link
- * ConnectivityService} will tear down all but the first connection.
+ * An L2CAP server network allocates a Protocol/Service Multiplexer (PSM) to be advertised to the
+ * client. A new server network must always be reserved using {@code
+ * ConnectivityManager#reserveNetwork}. The subsequent {@link
+ * ConnectivityManager.NetworkCallback#onReserved(NetworkCapabilities)} callback includes an {@code
+ * L2CapNetworkSpecifier}. The {@link getPsm()} method will return the Protocol/Service Multiplexer
+ * (PSM) of the reserved network so that the server can advertise it to the client and the client
+ * can connect.
+ * An L2CAP server network is backed by a {@link android.bluetooth.BluetoothServerSocket} which can,
+ * in theory, accept many connections. However, before SDK version {@link
+ * Build.VERSION_CODES.VANILLA_ICE_CREAM} Bluetooth APIs do not expose the channel ID, so these
+ * connections are indistinguishable. In practice, this means that the network matching semantics in
+ * ConnectivityService will tear down all but the first connection.
*
- * The L2cap client network can be connected using {@link ConnectivityManager#requestNetwork}
- * including passing in the relevant information (i.e. PSM and destination MAC address) using the
- * {@link L2capNetworkSpecifier}.
- *
+ * When the connection between client and server completes, a {@link Network} whose capabilities
+ * satisfy this {@code L2capNetworkSpecifier} will connect and the usual callbacks, such as {@link
+ * NetworkCallback#onAvailable}, will be called on the callback object passed to {@code
+ * ConnectivityManager#reserveNetwork} or {@code ConnectivityManager#requestNetwork}.
*/
@FlaggedApi(Flags.FLAG_IPV6_OVER_BLE)
public final class L2capNetworkSpecifier extends NetworkSpecifier implements Parcelable {
- /** Accept any role. */
+ /**
+ * Match any role.
+ *
+ * This role is only meaningful in {@link NetworkRequest}s. Specifiers for actual L2CAP
+ * networks never have this role set.
+ */
public static final int ROLE_ANY = 0;
- /** Specifier describes a client network. */
+ /** Specifier describes a client network, i.e., the device is the Bluetooth central. */
public static final int ROLE_CLIENT = 1;
- /** Specifier describes a server network. */
+ /** Specifier describes a server network, i.e., the device is the Bluetooth peripheral. */
public static final int ROLE_SERVER = 2;
/** @hide */
@@ -72,7 +86,12 @@
@Role
private final int mRole;
- /** Accept any form of header compression. */
+ /**
+ * Accept any form of header compression.
+ *
+ * This option is only meaningful in {@link NetworkRequest}s. Specifiers for actual L2CAP
+ * networks never have this option set.
+ */
public static final int HEADER_COMPRESSION_ANY = 0;
/** Do not compress packets on this network. */
public static final int HEADER_COMPRESSION_NONE = 1;
@@ -91,16 +110,19 @@
@HeaderCompression
private final int mHeaderCompression;
- /**
- * The MAC address of the remote.
- */
+ /** The MAC address of the remote. */
@Nullable
private final MacAddress mRemoteAddress;
- /** Match any PSM. */
- public static final int PSM_ANY = -1;
+ /**
+ * Match any Protocol/Service Multiplexer (PSM).
+ *
+ * This PSM value is only meaningful in {@link NetworkRequest}s. Specifiers for actual L2CAP
+ * networks never have this value set.
+ */
+ public static final int PSM_ANY = 0;
- /** The Bluetooth L2CAP Protocol Service Multiplexer (PSM). */
+ /** The Bluetooth L2CAP Protocol/Service Multiplexer (PSM). */
private final int mPsm;
private L2capNetworkSpecifier(Parcel in) {
@@ -131,12 +153,19 @@
return mHeaderCompression;
}
- /** Returns the remote MAC address for this network to connect to. */
+ /**
+ * Returns the remote MAC address for this network to connect to.
+ *
+ * The remote address is only meaningful for networks that have ROLE_CLIENT.
+ *
+ * When receiving this {@link L2capNetworkSpecifier} from Connectivity APIs such as a {@link
+ * ConnectivityManager.NetworkCallback}, the MAC address is redacted.
+ */
public @Nullable MacAddress getRemoteAddress() {
return mRemoteAddress;
}
- /** Returns the PSM for this network to connect to. */
+ /** Returns the Protocol/Service Multiplexer (PSM) for this network to connect to. */
public int getPsm() {
return mPsm;
}
@@ -144,9 +173,9 @@
/** A builder class for L2capNetworkSpecifier. */
public static final class Builder {
@Role
- private int mRole;
+ private int mRole = ROLE_ANY;
@HeaderCompression
- private int mHeaderCompression;
+ private int mHeaderCompression = HEADER_COMPRESSION_ANY;
@Nullable
private MacAddress mRemoteAddress;
private int mPsm = PSM_ANY;
@@ -154,6 +183,8 @@
/**
* Set the role to use for this network.
*
+ * If not set, defaults to {@link ROLE_ANY}.
+ *
* @param role the role to use.
*/
@NonNull
@@ -165,6 +196,10 @@
/**
* Set the header compression mechanism to use for this network.
*
+ * If not set, defaults to {@link HEADER_COMPRESSION_ANY}. This option must be specified
+ * (i.e. must not be set to {@link HEADER_COMPRESSION_ANY}) when requesting or reserving a
+ * new network.
+ *
* @param headerCompression the header compression mechanism to use.
*/
@NonNull
@@ -176,26 +211,28 @@
/**
* Set the remote address for the client to connect to.
*
- * Only valid for client networks. A null MacAddress matches *any* MacAddress.
+ * Only valid for client networks. If not set, the specifier matches any MAC address.
*
- * @param remoteAddress the MAC address to connect to.
+ * @param remoteAddress the MAC address to connect to, or null to match any MAC address.
*/
@NonNull
- public Builder setRemoteAddress(@NonNull MacAddress remoteAddress) {
- Objects.requireNonNull(remoteAddress);
+ public Builder setRemoteAddress(@Nullable MacAddress remoteAddress) {
mRemoteAddress = remoteAddress;
return this;
}
/**
- * Set the PSM for the client to connect to.
+ * Set the Protocol/Service Multiplexer (PSM) for the client to connect to.
*
- * Can only be configured on client networks.
+ * If not set, defaults to {@link PSM_ANY}.
*
- * @param psm the Protocol Service Multiplexer (PSM) to connect to.
+ * @param psm the Protocol/Service Multiplexer (PSM) to connect to.
*/
@NonNull
- public Builder setPsm(int psm) {
+ public Builder setPsm(@IntRange(from = 0, to = 255) int psm) {
+ if (psm < 0 /* PSM_ANY */ || psm > 0xFF) {
+ throw new IllegalArgumentException("PSM must be PSM_ANY or within range [1, 255]");
+ }
mPsm = psm;
return this;
}
@@ -203,7 +240,10 @@
/** Create the L2capNetworkSpecifier object. */
@NonNull
public L2capNetworkSpecifier build() {
- // TODO: throw an exception for combinations that cannot be supported.
+ if (mRole == ROLE_SERVER && mRemoteAddress != null) {
+ throw new IllegalArgumentException(
+ "Specifying a remote address is not valid for server role.");
+ }
return new L2capNetworkSpecifier(mRole, mHeaderCompression, mRemoteAddress, mPsm);
}
}
@@ -211,21 +251,41 @@
/** @hide */
@Override
public boolean canBeSatisfiedBy(NetworkSpecifier other) {
- // TODO: implement matching semantics.
- return false;
+ if (!(other instanceof L2capNetworkSpecifier)) return false;
+ final L2capNetworkSpecifier rhs = (L2capNetworkSpecifier) other;
+
+ // A network / offer cannot be ROLE_ANY, but it is added for consistency.
+ if (mRole != rhs.mRole && mRole != ROLE_ANY && rhs.mRole != ROLE_ANY) {
+ return false;
+ }
+
+ if (mHeaderCompression != rhs.mHeaderCompression
+ && mHeaderCompression != HEADER_COMPRESSION_ANY
+ && rhs.mHeaderCompression != HEADER_COMPRESSION_ANY) {
+ return false;
+ }
+
+ if (!Objects.equals(mRemoteAddress, rhs.mRemoteAddress)
+ && mRemoteAddress != null && rhs.mRemoteAddress != null) {
+ return false;
+ }
+
+ if (mPsm != rhs.mPsm && mPsm != PSM_ANY && rhs.mPsm != PSM_ANY) {
+ return false;
+ }
+ return true;
}
/** @hide */
@Override
@Nullable
public NetworkSpecifier redact() {
- // Redact the remote MAC address and the PSM (for non-server roles).
final NetworkSpecifier redactedSpecifier = new Builder()
.setRole(mRole)
.setHeaderCompression(mHeaderCompression)
- // TODO: consider not redacting the specifier in onReserved, so the redaction can be
- // more strict (i.e. the PSM could always be redacted).
- .setPsm(mRole == ROLE_SERVER ? mPsm : PSM_ANY)
+ // The remote address is redacted.
+ .setRemoteAddress(null)
+ .setPsm(mPsm)
.build();
return redactedSpecifier;
}
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
index 66ae79c..ac381b8 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
@@ -89,6 +89,9 @@
break;
case BroadcastRequest.PRESENCE_VERSION_V1:
if (adapter.isLeExtendedAdvertisingSupported()) {
+ if (mAdvertisingSetCallback == null) {
+ mAdvertisingSetCallback = getAdvertisingSetCallback();
+ }
bluetoothLeAdvertiser.startAdvertisingSet(
getAdvertisingSetParameters(),
advertiseData,
@@ -133,6 +136,11 @@
}
mBroadcastListener = null;
mIsAdvertising = false;
+ // If called startAdvertisingSet() but onAdvertisingSetStopped() is not invoked yet,
+ // using the same mAdvertisingSetCallback will cause new advertising cann't be stopped.
+ // Therefore, release the old mAdvertisingSetCallback and
+ // create a new mAdvertisingSetCallback when calling startAdvertisingSet.
+ mAdvertisingSetCallback = null;
}
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
index 764300c..ce14fc6 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -29,6 +29,7 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
+import android.provider.DeviceConfig;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -231,25 +232,17 @@
}
boolean success = false;
- boolean failureLogged = false;
+ int failureReason = -1;
try {
success = mSignatureVerifier.verify(contentUri, metadataUri);
} catch (MissingPublicKeyException e) {
if (updateFailureCount()) {
- failureLogged = true;
- mLogger.logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND,
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
+ failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND;
}
} catch (InvalidKeyException e) {
if (updateFailureCount()) {
- failureLogged = true;
- mLogger.logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
- mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
+ failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
}
} catch (IOException | GeneralSecurityException e) {
Log.e(TAG, "Could not verify new log list", e);
@@ -259,9 +252,13 @@
Log.w(TAG, "Log list did not pass verification");
// Avoid logging failure twice
- if (!failureLogged && updateFailureCount()) {
+ if (failureReason == -1 && updateFailureCount()) {
+ failureReason = CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION;
+ }
+
+ if (failureReason != -1) {
mLogger.logCTLogListUpdateFailedEvent(
- CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
+ failureReason,
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
}
@@ -324,7 +321,12 @@
mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, new_failure_count);
mDataStore.store();
- boolean shouldReport = new_failure_count >= Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD;
+ int threshold = DeviceConfig.getInt(
+ Config.NAMESPACE_NETWORK_SECURITY,
+ Config.FLAG_LOG_FAILURE_THRESHOLD,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+
+ boolean shouldReport = new_failure_count >= threshold;
if (shouldReport) {
Log.d(TAG, "Log list update failure count exceeds threshold: " + new_failure_count);
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/Config.java b/networksecurity/service/src/com/android/server/net/ct/Config.java
index 592bc4e..bc4efab 100644
--- a/networksecurity/service/src/com/android/server/net/ct/Config.java
+++ b/networksecurity/service/src/com/android/server/net/ct/Config.java
@@ -45,6 +45,7 @@
static final String FLAG_METADATA_URL = FLAGS_PREFIX + "metadata_url";
static final String FLAG_VERSION = FLAGS_PREFIX + "version";
static final String FLAG_PUBLIC_KEY = FLAGS_PREFIX + "public_key";
+ static final String FLAG_LOG_FAILURE_THRESHOLD = FLAGS_PREFIX + "log_list_failure_threshold";
// properties
static final String VERSION = "version";
@@ -60,5 +61,5 @@
static final String URL_PUBLIC_KEY = URL_PREFIX + "log_list.pub";
// Threshold amounts
- static final int LOG_LIST_UPDATE_FAILURE_THRESHOLD = 10;
+ static final int DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD = 10;
}
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
index 34f8dd1..2f57fc9 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
@@ -203,7 +203,8 @@
mCertificateTransparencyDownloader.startPublicKeyDownload();
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -212,11 +213,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
.logCTLogListUpdateFailedEventWithDownloadStatus(
DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
@@ -273,7 +274,8 @@
mCertificateTransparencyDownloader.startMetadataDownload();
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -283,11 +285,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
.logCTLogListUpdateFailedEventWithDownloadStatus(
DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
@@ -350,7 +352,8 @@
mCertificateTransparencyDownloader.startContentDownload(mCompatVersion);
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.onReceive(
mContext,
@@ -360,11 +363,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
.logCTLogListUpdateFailedEventWithDownloadStatus(
DownloadManager.ERROR_INSUFFICIENT_SPACE,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
@@ -415,7 +418,8 @@
mCertificateTransparencyDownloader.startMetadataDownload();
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
// Set the public key to be missing
mSignatureVerifier.resetPublicKey();
@@ -427,11 +431,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
.logCTLogListUpdateFailedEvent(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_NOT_FOUND,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, never())
.logCTLogListUpdateFailedEvent(
eq(
@@ -453,7 +457,8 @@
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
// Act
mCertificateTransparencyDownloader.startMetadataDownload();
@@ -466,7 +471,7 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, never())
.logCTLogListUpdateFailedEvent(
eq(
@@ -475,7 +480,7 @@
verify(mLogger, times(1))
.logCTLogListUpdateFailedEvent(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
@@ -492,7 +497,8 @@
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
// Act
mCertificateTransparencyDownloader.startMetadataDownload();
@@ -505,7 +511,7 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, never())
.logCTLogListUpdateFailedEvent(
eq(
@@ -514,7 +520,7 @@
verify(mLogger, times(1))
.logCTLogListUpdateFailedEvent(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_SIGNATURE_VERIFICATION,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
@@ -560,7 +566,8 @@
mSignatureVerifier.setPublicKey(mPublicKey);
// Set the failure count to just below the threshold
mDataStore.setPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
mCertificateTransparencyDownloader.startMetadataDownload();
mCertificateTransparencyDownloader.onReceive(
@@ -571,11 +578,11 @@
assertThat(
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
- .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ .isEqualTo(Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
verify(mLogger, times(1))
.logCTLogListUpdateFailedEvent(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_FAILED__FAILURE_REASON__FAILURE_VERSION_ALREADY_EXISTS,
- Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ Config.DEFAULT_LOG_LIST_UPDATE_FAILURE_THRESHOLD);
}
@Test
diff --git a/service/ServiceConnectivityResources/res/values/config_thread.xml b/service/ServiceConnectivityResources/res/values/config_thread.xml
index 4027038..d1d9e52 100644
--- a/service/ServiceConnectivityResources/res/values/config_thread.xml
+++ b/service/ServiceConnectivityResources/res/values/config_thread.xml
@@ -71,4 +71,18 @@
-->
<string-array name="config_thread_mdns_vendor_specific_txts">
</string-array>
+
+ <!-- Whether to enable / start SRP server only when border routing is ready. SRP server and
+ border routing are mandatory features required by a Thread Border Router, and it takes 10 to
+ 20 seconds to establish border routing. Starting SRP server earlier is useful for use cases
+ where the user needs to know what are the devices in the network before actually needs to reach
+ to the devices, or reaching to Thread end devices doesn't require border routing to work.
+ -->
+ <bool name="config_thread_srp_server_wait_for_border_routing_enabled">true</bool>
+
+ <!-- Whether this border router will automatically join the previous connected network after
+ device reboots. Setting this value to false can allow the user to control the lifecycle of
+ the Thread border router state on this device.
+ -->
+ <bool name="config_thread_border_router_auto_join_enabled">true</bool>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index fbaae05..7ac86aa 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -52,6 +52,8 @@
<item type="string" name="config_thread_vendor_oui" />
<item type="string" name="config_thread_model_name" />
<item type="array" name="config_thread_mdns_vendor_specific_txts" />
+ <item type="bool" name="config_thread_srp_server_wait_for_border_routing_enabled" />
+ <item type="bool" name="config_thread_border_router_auto_join_enabled" />
</policy>
</overlayable>
</resources>
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
new file mode 100644
index 0000000..34968e7
--- /dev/null
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN;
+import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_ANY;
+import static android.net.L2capNetworkSpecifier.PSM_ANY;
+import static android.net.L2capNetworkSpecifier.ROLE_SERVER;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.L2capNetworkSpecifier;
+import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
+import android.net.NetworkProvider.NetworkOfferCallback;
+import android.net.NetworkRequest;
+import android.net.NetworkScore;
+import android.net.NetworkSpecifier;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Map;
+
+
+public class L2capNetworkProvider {
+ private static final String TAG = L2capNetworkProvider.class.getSimpleName();
+ private final Dependencies mDeps;
+ private final Handler mHandler;
+ private final NetworkProvider mProvider;
+ private final BlanketReservationOffer mBlanketOffer;
+ private final Map<Integer, ReservedServerOffer> mReservedServerOffers = new ArrayMap<>();
+
+ /**
+ * The blanket reservation offer is used to create an L2CAP server network, i.e. a network
+ * based on a BluetoothServerSocket.
+ *
+ * Note that NetworkCapabilities matching semantics will cause onNetworkNeeded to be called for
+ * requests that do not have a NetworkSpecifier set.
+ */
+ private class BlanketReservationOffer implements NetworkOfferCallback {
+ // TODO: ensure that once the incoming request is satisfied, the blanket offer does not get
+ // unneeded. This means the blanket offer must always outscore the reserved offer. This
+ // might require setting the blanket offer as setTransportPrimary().
+ public static final NetworkScore SCORE = new NetworkScore.Builder().build();
+ // Note the missing NET_CAPABILITY_NOT_RESTRICTED marking the network as restricted.
+ public static final NetworkCapabilities CAPABILITIES;
+ static {
+ final L2capNetworkSpecifier l2capNetworkSpecifier = new L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .build();
+ NetworkCapabilities caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .setNetworkSpecifier(l2capNetworkSpecifier)
+ .build();
+ caps.setReservationId(RES_ID_MATCH_ALL_RESERVATIONS);
+ CAPABILITIES = caps;
+ }
+
+ // TODO: consider moving this into L2capNetworkSpecifier as #isValidServerReservation().
+ private boolean isValidL2capSpecifier(@Nullable NetworkSpecifier spec) {
+ if (spec == null) return false;
+ // If spec is not null, L2capNetworkSpecifier#canBeSatisfiedBy() guarantees the
+ // specifier is of type L2capNetworkSpecifier.
+ final L2capNetworkSpecifier l2capSpec = (L2capNetworkSpecifier) spec;
+
+ // The ROLE_SERVER offer can be satisfied by a ROLE_ANY request.
+ if (l2capSpec.getRole() != ROLE_SERVER) return false;
+
+ // HEADER_COMPRESSION_ANY is never valid in a request.
+ if (l2capSpec.getHeaderCompression() == HEADER_COMPRESSION_ANY) return false;
+
+ // remoteAddr must be null for ROLE_SERVER requests.
+ if (l2capSpec.getRemoteAddress() != null) return false;
+
+ // reservation must allocate a PSM, so only PSM_ANY can be passed.
+ if (l2capSpec.getPsm() != PSM_ANY) return false;
+
+ return true;
+ }
+
+ @Override
+ public void onNetworkNeeded(NetworkRequest request) {
+ Log.d(TAG, "New reservation request: " + request);
+ if (!isValidL2capSpecifier(request.getNetworkSpecifier())) {
+ Log.w(TAG, "Ignoring invalid reservation request: " + request);
+ return;
+ }
+
+ final NetworkCapabilities reservationCaps = request.networkCapabilities;
+ final ReservedServerOffer reservedOffer = new ReservedServerOffer(reservationCaps);
+
+ final NetworkCapabilities reservedCaps = reservedOffer.getReservedCapabilities();
+ mProvider.registerNetworkOffer(SCORE, reservedCaps, mHandler::post, reservedOffer);
+ mReservedServerOffers.put(request.requestId, reservedOffer);
+ }
+
+ @Override
+ public void onNetworkUnneeded(NetworkRequest request) {
+ if (!mReservedServerOffers.containsKey(request.requestId)) {
+ return;
+ }
+
+ final ReservedServerOffer reservedOffer = mReservedServerOffers.get(request.requestId);
+ // Note that the reserved offer gets torn down when the reservation goes away, even if
+ // there are lingering requests.
+ reservedOffer.tearDown();
+ mProvider.unregisterNetworkOffer(reservedOffer);
+ }
+ }
+
+ private class ReservedServerOffer implements NetworkOfferCallback {
+ private final boolean mUseHeaderCompression;
+ private final int mPsm;
+ private final NetworkCapabilities mReservedCapabilities;
+
+ public ReservedServerOffer(NetworkCapabilities reservationCaps) {
+ // getNetworkSpecifier() is guaranteed to return a non-null L2capNetworkSpecifier.
+ final L2capNetworkSpecifier reservationSpec =
+ (L2capNetworkSpecifier) reservationCaps.getNetworkSpecifier();
+ mUseHeaderCompression =
+ reservationSpec.getHeaderCompression() == HEADER_COMPRESSION_6LOWPAN;
+
+ // TODO: open BluetoothServerSocket and allocate a PSM.
+ mPsm = 0x80;
+
+ final L2capNetworkSpecifier reservedSpec = new L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(reservationSpec.getHeaderCompression())
+ .setPsm(mPsm)
+ .build();
+ mReservedCapabilities = new NetworkCapabilities.Builder(reservationCaps)
+ .setNetworkSpecifier(reservedSpec)
+ .build();
+ }
+
+ public NetworkCapabilities getReservedCapabilities() {
+ return mReservedCapabilities;
+ }
+
+ @Override
+ public void onNetworkNeeded(NetworkRequest request) {
+ // TODO: implement
+ }
+
+ @Override
+ public void onNetworkUnneeded(NetworkRequest request) {
+ // TODO: implement
+ }
+
+ /**
+ * Called when the reservation goes away and the reserved offer must be torn down.
+ *
+ * This method can be called multiple times.
+ */
+ public void tearDown() {
+ // TODO: implement.
+ // This method can be called multiple times.
+ }
+ }
+
+ @VisibleForTesting
+ public static class Dependencies {
+ /** Get NetworkProvider */
+ public NetworkProvider getNetworkProvider(Context context, Looper looper) {
+ return new NetworkProvider(context, looper, TAG);
+ }
+ }
+
+ public L2capNetworkProvider(Context context, Handler handler) {
+ this(new Dependencies(), context, handler);
+ }
+
+ @VisibleForTesting
+ public L2capNetworkProvider(Dependencies deps, Context context, Handler handler) {
+ mDeps = deps;
+ mHandler = handler;
+ mProvider = mDeps.getNetworkProvider(context, handler.getLooper());
+ mBlanketOffer = new BlanketReservationOffer();
+
+ final boolean isBleSupported =
+ context.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH_LE);
+ if (isBleSupported) {
+ context.getSystemService(ConnectivityManager.class).registerNetworkProvider(mProvider);
+ mProvider.registerNetworkOffer(BlanketReservationOffer.SCORE,
+ BlanketReservationOffer.CAPABILITIES, mHandler::post, mBlanketOffer);
+ }
+ }
+}
+
diff --git a/tests/cts/multidevices/apfv4_test.py b/tests/cts/multidevices/apfv4_test.py
index 7795be5..aa535fd 100644
--- a/tests/cts/multidevices/apfv4_test.py
+++ b/tests/cts/multidevices/apfv4_test.py
@@ -53,7 +53,7 @@
) # Declare inputs for state_str and expected_result.
def test_apf_drop_ethertype_not_allowed(self, blocked_ether_type):
# Ethernet header (14 bytes).
- packet = ETHER_BROADCAST_ADDR # Destination MAC (broadcast)
+ packet = self.client_mac_address.replace(":", "") # Destination MAC
packet += self.server_mac_address.replace(":", "") # Source MAC
packet += blocked_ether_type
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index dd0665e..324078a 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -22,6 +22,7 @@
import android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.content.pm.PackageManager.FEATURE_AUTOMOTIVE
+import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_WIFI
import android.net.ConnectivityManager
import android.net.Network
@@ -37,7 +38,7 @@
import android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET
import android.net.apf.ApfConstants.IPV6_SRC_ADDR_OFFSET
import android.net.apf.ApfCounterTracker
-import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_MULTICAST_PING
+import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_NS_REPLIED_NON_DAD
import android.net.apf.ApfCounterTracker.Counter.FILTER_AGE_16384THS
import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_ICMP
import android.net.apf.ApfV4Generator
@@ -103,6 +104,7 @@
import kotlin.test.assertNotNull
import org.junit.After
import org.junit.AfterClass
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Rule
@@ -298,10 +300,23 @@
return ApfCapabilities(version, maxLen, packetFormat)
}
+ private fun isTvDeviceSupportFullNetworkingUnder2w(): Boolean {
+ return (pm.hasSystemFeature(FEATURE_LEANBACK) &&
+ pm.hasSystemFeature("com.google.android.tv.full_networking_under_2w"))
+ }
+
@Before
fun setUp() {
assume().that(pm.hasSystemFeature(FEATURE_WIFI)).isTrue()
+ // Based on GTVS-16, Android Packet Filtering (APF) is OPTIONAL for devices that fully
+ // process all network packets on CPU at all times, even in standby, while meeting
+ // the <= 2W standby power demand requirement.
+ assumeFalse(
+ "Skipping test: TV device process full networking on CPU under 2W",
+ isTvDeviceSupportFullNetworkingUnder2w()
+ )
+
networkCallback = TestableNetworkCallback()
cm.requestNetwork(
NetworkRequest.Builder()
@@ -348,10 +363,8 @@
@Test
fun testApfCapabilities() {
// APF became mandatory in Android 14 VSR.
- assume().that(getVsrApiLevel()).isAtLeast(34)
-
- // ApfFilter does not support anything but ARPHRD_ETHER.
- assertThat(caps.apfPacketFormat).isEqualTo(OsConstants.ARPHRD_ETHER)
+ val vsrApiLevel = getVsrApiLevel()
+ assume().that(vsrApiLevel).isAtLeast(34)
// DEVICEs launching with Android 14 with CHIPSETs that set ro.board.first_api_level to 34:
// - [GMS-VSR-5.3.12-003] MUST return 4 or higher as the APF version number from calls to
@@ -371,9 +384,22 @@
// ro.board.first_api_level or ro.board.api_level to 202404 or higher:
// - [GMS-VSR-5.3.12-009] MUST indicate at least 2048 bytes of usable memory from calls to
// the getApfPacketFilterCapabilities HAL method.
- if (getVsrApiLevel() >= 202404) {
+ if (vsrApiLevel >= 202404) {
assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
}
+
+ // CHIPSETs (or DEVICES with CHIPSETs) that set ro.board.first_api_level or
+ // ro.board.api_level to 202504 or higher:
+ // - [VSR-5.3.12-018] MUST implement version 6 of the Android Packet Filtering (APF)
+ // interpreter in the Wi-Fi firmware.
+ // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM.
+ if (vsrApiLevel >= 202504) {
+ assertThat(caps.apfVersionSupported).isEqualTo(6000)
+ assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+ }
+
+ // ApfFilter does not support anything but ARPHRD_ETHER.
+ assertThat(caps.apfPacketFormat).isEqualTo(OsConstants.ARPHRD_ETHER)
}
// APF is backwards compatible, i.e. a v6 interpreter supports both v2 and v4 functionality.
@@ -690,7 +716,7 @@
// pass
// else
// transmit a ICMPv6 echo request packet with the first byte of the payload in the reply
- // increase DROPPED_IPV6_MULTICAST_PING counter
+ // increase DROPPED_IPV6_NS_REPLIED_NON_DAD counter
// drop
val program = gen
.addLoad16(R0, ETH_ETHERTYPE_OFFSET)
@@ -732,8 +758,8 @@
IPPROTO_ICMPV6, // partial_sum
false // udp
)
- // Warning: the program abuse DROPPED_IPV6_MULTICAST_PING for debugging purpose
- .addCountAndDrop(DROPPED_IPV6_MULTICAST_PING)
+ // Warning: the program abuse DROPPED_IPV6_NS_REPLIED_NON_DAD for debugging purpose
+ .addCountAndDrop(DROPPED_IPV6_NS_REPLIED_NON_DAD)
.defineLabel(skipPacketLabel)
.addPass()
.generate()
diff --git a/tests/cts/net/src/android/net/cts/L2capNetworkSpecifierTest.kt b/tests/cts/net/src/android/net/cts/L2capNetworkSpecifierTest.kt
index b593baf..484cce8 100644
--- a/tests/cts/net/src/android/net/cts/L2capNetworkSpecifierTest.kt
+++ b/tests/cts/net/src/android/net/cts/L2capNetworkSpecifierTest.kt
@@ -18,7 +18,9 @@
import android.net.L2capNetworkSpecifier
import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN
+import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_ANY
import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE
+import android.net.L2capNetworkSpecifier.PSM_ANY
import android.net.L2capNetworkSpecifier.ROLE_CLIENT
import android.net.L2capNetworkSpecifier.ROLE_SERVER
import android.net.MacAddress
@@ -28,6 +30,8 @@
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.assertParcelingIsLossless
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,14 +55,63 @@
fun testGetters() {
val remoteMac = MacAddress.fromString("11:22:33:44:55:66")
val specifier = L2capNetworkSpecifier.Builder()
- .setRole(ROLE_SERVER)
+ .setRole(ROLE_CLIENT)
.setHeaderCompression(HEADER_COMPRESSION_NONE)
.setPsm(123)
.setRemoteAddress(remoteMac)
.build()
- assertEquals(ROLE_SERVER, specifier.getRole())
+ assertEquals(ROLE_CLIENT, specifier.getRole())
assertEquals(HEADER_COMPRESSION_NONE, specifier.getHeaderCompression())
assertEquals(123, specifier.getPsm())
assertEquals(remoteMac, specifier.getRemoteAddress())
}
+
+ @Test
+ fun testCanBeSatisfiedBy() {
+ val blanketOffer = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_ANY)
+ .setPsm(PSM_ANY)
+ .build()
+
+ val reservedOffer = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .setPsm(42)
+ .build()
+
+ val clientOffer = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_CLIENT)
+ .setHeaderCompression(HEADER_COMPRESSION_ANY)
+ .build()
+
+ val serverReservation = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+
+ assertTrue(serverReservation.canBeSatisfiedBy(blanketOffer))
+ assertTrue(serverReservation.canBeSatisfiedBy(reservedOffer))
+ // Note: serverReservation can be filed using reserveNetwork, or it could be a regular
+ // request filed using requestNetwork.
+ assertFalse(serverReservation.canBeSatisfiedBy(clientOffer))
+
+ val clientRequest = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_CLIENT)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .setRemoteAddress(MacAddress.fromString("00:01:02:03:04:05"))
+ .setPsm(42)
+ .build()
+
+ assertTrue(clientRequest.canBeSatisfiedBy(clientOffer))
+ // Note: the BlanketOffer also includes a RES_ID_MATCH_ALL_RESERVATIONS. Since the
+ // clientRequest is not a reservation, it won't match that request to begin with.
+ assertFalse(clientRequest.canBeSatisfiedBy(blanketOffer))
+ assertFalse(clientRequest.canBeSatisfiedBy(reservedOffer))
+
+ val matchAny = L2capNetworkSpecifier.Builder().build()
+ assertTrue(matchAny.canBeSatisfiedBy(blanketOffer))
+ assertTrue(matchAny.canBeSatisfiedBy(reservedOffer))
+ assertTrue(matchAny.canBeSatisfiedBy(clientOffer))
+ }
}
diff --git a/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt b/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt
new file mode 100644
index 0000000..ffa9828
--- /dev/null
+++ b/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.TYPE_NONE
+import android.net.L2capNetworkSpecifier
+import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN
+import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE
+import android.net.L2capNetworkSpecifier.ROLE_SERVER
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
+import android.net.NetworkProvider
+import android.net.NetworkProvider.NetworkOfferCallback
+import android.net.NetworkRequest
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+const val TAG = "L2capNetworkProviderTest"
+
+val RESERVATION_CAPS = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .build()
+
+val RESERVATION = NetworkRequest(
+ NetworkCapabilities(RESERVATION_CAPS),
+ TYPE_NONE,
+ 42 /* rId */,
+ NetworkRequest.Type.RESERVATION
+)
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class L2capNetworkProviderTest {
+ @Mock private lateinit var context: Context
+ @Mock private lateinit var deps: L2capNetworkProvider.Dependencies
+ @Mock private lateinit var provider: NetworkProvider
+ @Mock private lateinit var cm: ConnectivityManager
+ @Mock private lateinit var pm: PackageManager
+
+ private val handlerThread = HandlerThread("$TAG handler thread").apply { start() }
+ private val handler = Handler(handlerThread.looper)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ doReturn(provider).`when`(deps).getNetworkProvider(any(), any())
+ doReturn(cm).`when`(context).getSystemService(eq(ConnectivityManager::class.java))
+ doReturn(pm).`when`(context).getPackageManager()
+ doReturn(true).`when`(pm).hasSystemFeature(FEATURE_BLUETOOTH_LE)
+ }
+
+ @After
+ fun tearDown() {
+ handlerThread.quitSafely()
+ handlerThread.join()
+ }
+
+ @Test
+ fun testNetworkProvider_registeredWhenSupported() {
+ L2capNetworkProvider(deps, context, handler)
+ verify(cm).registerNetworkProvider(eq(provider))
+ verify(provider).registerNetworkOffer(any(), any(), any(), any())
+ }
+
+ @Test
+ fun testNetworkProvider_notRegisteredWhenNotSupported() {
+ doReturn(false).`when`(pm).hasSystemFeature(FEATURE_BLUETOOTH_LE)
+ L2capNetworkProvider(deps, context, handler)
+ verify(cm, never()).registerNetworkProvider(eq(provider))
+ }
+
+ fun doTestBlanketOfferIgnoresRequest(request: NetworkRequest) {
+ clearInvocations(provider)
+ L2capNetworkProvider(deps, context, handler)
+
+ val blanketOfferCaptor = ArgumentCaptor.forClass(NetworkOfferCallback::class.java)
+ verify(provider).registerNetworkOffer(any(), any(), any(), blanketOfferCaptor.capture())
+
+ blanketOfferCaptor.value.onNetworkNeeded(request)
+ verify(provider).registerNetworkOffer(any(), any(), any(), any())
+ }
+
+ fun doTestBlanketOfferCreatesReservation(
+ request: NetworkRequest,
+ reservation: NetworkCapabilities
+ ) {
+ clearInvocations(provider)
+ L2capNetworkProvider(deps, context, handler)
+
+ val blanketOfferCaptor = ArgumentCaptor.forClass(NetworkOfferCallback::class.java)
+ verify(provider).registerNetworkOffer(any(), any(), any(), blanketOfferCaptor.capture())
+
+ blanketOfferCaptor.value.onNetworkNeeded(request)
+
+ val capsCaptor = ArgumentCaptor.forClass(NetworkCapabilities::class.java)
+ verify(provider, times(2)).registerNetworkOffer(any(), capsCaptor.capture(), any(), any())
+
+ assertTrue(reservation.satisfiedByNetworkCapabilities(capsCaptor.value))
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithoutSpecifier() {
+ val caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .build()
+ val nr = NetworkRequest(caps, TYPE_NONE, 42 /* rId */, NetworkRequest.Type.RESERVATION)
+
+ doTestBlanketOfferIgnoresRequest(nr)
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithCorrectSpecifier() {
+ var specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+ var caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ var nr = NetworkRequest(caps, TYPE_NONE, 42 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferCreatesReservation(nr, caps)
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .build()
+ caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ nr = NetworkRequest(caps, TYPE_NONE, 43 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferCreatesReservation(nr, caps)
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithIncorrectSpecifier() {
+ var specifier = L2capNetworkSpecifier.Builder().build()
+ var caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ var nr = NetworkRequest(caps, TYPE_NONE, 42 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferIgnoresRequest(nr)
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .build()
+ caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ nr = NetworkRequest(caps, TYPE_NONE, 44 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferIgnoresRequest(nr)
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .setPsm(0x81)
+ .build()
+ caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ nr = NetworkRequest(caps, TYPE_NONE, 45 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferIgnoresRequest(nr)
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .build()
+ caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .setNetworkSpecifier(specifier)
+ .build()
+ nr = NetworkRequest(caps, TYPE_NONE, 47 /* rId */, NetworkRequest.Type.RESERVATION)
+ doTestBlanketOfferIgnoresRequest(nr)
+ }
+}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index c55096b..af16d19 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -623,12 +623,17 @@
mNat64CidrController.maybeUpdateNat64Cidr();
}
- private static OtDaemonConfiguration newOtDaemonConfig(
- @NonNull ThreadConfiguration threadConfig) {
+ private OtDaemonConfiguration newOtDaemonConfig(ThreadConfiguration threadConfig) {
+ int srpServerConfig = R.bool.config_thread_srp_server_wait_for_border_routing_enabled;
+ boolean srpServerWaitEnabled = mResources.get().getBoolean(srpServerConfig);
+ int autoJoinConfig = R.bool.config_thread_border_router_auto_join_enabled;
+ boolean autoJoinEnabled = mResources.get().getBoolean(autoJoinConfig);
return new OtDaemonConfiguration.Builder()
.setBorderRouterEnabled(threadConfig.isBorderRouterEnabled())
.setNat64Enabled(threadConfig.isNat64Enabled())
.setDhcpv6PdEnabled(threadConfig.isDhcpv6PdEnabled())
+ .setSrpServerWaitForBorderRoutingEnabled(srpServerWaitEnabled)
+ .setBorderRouterAutoJoinEnabled(autoJoinEnabled)
.build();
}
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index dcbb3f5..bc8da8b 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -231,6 +231,11 @@
when(mConnectivityResources.get()).thenReturn(mResources);
when(mResources.getBoolean(eq(R.bool.config_thread_default_enabled))).thenReturn(true);
+ when(mResources.getBoolean(
+ eq(R.bool.config_thread_srp_server_wait_for_border_routing_enabled)))
+ .thenReturn(true);
+ when(mResources.getBoolean(eq(R.bool.config_thread_border_router_auto_join_enabled)))
+ .thenReturn(true);
when(mResources.getString(eq(R.string.config_thread_vendor_name)))
.thenReturn(TEST_VENDOR_NAME);
when(mResources.getString(eq(R.string.config_thread_vendor_oui)))
@@ -285,6 +290,11 @@
@Test
public void initialize_resourceOverlayValuesAreSetToOtDaemon() throws Exception {
+ when(mResources.getBoolean(
+ eq(R.bool.config_thread_srp_server_wait_for_border_routing_enabled)))
+ .thenReturn(false);
+ when(mResources.getBoolean(eq(R.bool.config_thread_border_router_auto_join_enabled)))
+ .thenReturn(false);
when(mResources.getString(eq(R.string.config_thread_vendor_name)))
.thenReturn(TEST_VENDOR_NAME);
when(mResources.getString(eq(R.string.config_thread_vendor_oui)))
@@ -297,6 +307,8 @@
mService.initialize();
mTestLooper.dispatchAll();
+ assertThat(mFakeOtDaemon.getConfiguration().srpServerWaitForBorderRoutingEnabled).isFalse();
+ assertThat(mFakeOtDaemon.getConfiguration().borderRouterAutoJoinEnabled).isFalse();
MeshcopTxtAttributes meshcopTxts = mFakeOtDaemon.getOverriddenMeshcopTxtAttributes();
assertThat(meshcopTxts.vendorName).isEqualTo(TEST_VENDOR_NAME);
assertThat(meshcopTxts.vendorOui).isEqualTo(TEST_VENDOR_OUI_BYTES);