Merge "Return error Status from BpfHandler::initPrograms()" into main
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 05cf9e8..3513573 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -378,11 +378,11 @@
package android.net.nsd {
public final class NsdManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void registerOffloadEngine(@NonNull String, long, long, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.OffloadEngine);
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void unregisterOffloadEngine(@NonNull android.net.nsd.OffloadEngine);
+ method @FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void registerOffloadEngine(@NonNull String, long, long, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.OffloadEngine);
+ method @FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void unregisterOffloadEngine(@NonNull android.net.nsd.OffloadEngine);
}
- public interface OffloadEngine {
+ @FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api") public interface OffloadEngine {
method public void onOffloadServiceRemoved(@NonNull android.net.nsd.OffloadServiceInfo);
method public void onOffloadServiceUpdated(@NonNull android.net.nsd.OffloadServiceInfo);
field public static final int OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK = 1; // 0x1
@@ -391,7 +391,7 @@
field public static final int OFFLOAD_TYPE_REPLY = 1; // 0x1
}
- public final class OffloadServiceInfo implements android.os.Parcelable {
+ @FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api") public final class OffloadServiceInfo implements android.os.Parcelable {
ctor public OffloadServiceInfo(@NonNull android.net.nsd.OffloadServiceInfo.Key, @NonNull java.util.List<java.lang.String>, @NonNull String, @Nullable byte[], @IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int, long);
method public int describeContents();
method @NonNull public String getHostname();
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index dae8914..bf01a9d 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -22,6 +22,7 @@
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -143,6 +144,14 @@
private static final String TAG = NsdManager.class.getSimpleName();
private static final boolean DBG = false;
+ // TODO : remove this class when udc-mainline-prod is abandoned and android.net.flags.Flags is
+ // available here
+ /** @hide */
+ public static class Flags {
+ static final String REGISTER_NSD_OFFLOAD_ENGINE_API =
+ "com.android.net.flags.register_nsd_offload_engine_api";
+ }
+
/**
* Broadcast intent action to indicate whether network service discovery is
* enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state
@@ -365,6 +374,7 @@
*
* @hide
*/
+ @FlaggedApi(NsdManager.Flags.REGISTER_NSD_OFFLOAD_ENGINE_API)
@SystemApi
@RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
NETWORK_STACK})
@@ -402,6 +412,7 @@
*
* @hide
*/
+ @FlaggedApi(NsdManager.Flags.REGISTER_NSD_OFFLOAD_ENGINE_API)
@SystemApi
@RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
NETWORK_STACK})
diff --git a/framework-t/src/android/net/nsd/OffloadEngine.java b/framework-t/src/android/net/nsd/OffloadEngine.java
index b566b13..9015985 100644
--- a/framework-t/src/android/net/nsd/OffloadEngine.java
+++ b/framework-t/src/android/net/nsd/OffloadEngine.java
@@ -16,6 +16,7 @@
package android.net.nsd;
+import android.annotation.FlaggedApi;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -33,6 +34,7 @@
*
* @hide
*/
+@FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api")
@SystemApi
public interface OffloadEngine {
/**
diff --git a/framework-t/src/android/net/nsd/OffloadServiceInfo.java b/framework-t/src/android/net/nsd/OffloadServiceInfo.java
index d5dbf19..98dc83a 100644
--- a/framework-t/src/android/net/nsd/OffloadServiceInfo.java
+++ b/framework-t/src/android/net/nsd/OffloadServiceInfo.java
@@ -16,6 +16,7 @@
package android.net.nsd;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +40,7 @@
*
* @hide
*/
+@FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api")
@SystemApi
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public final class OffloadServiceInfo implements Parcelable {
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 4e9087c..574ab2f 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -756,11 +756,20 @@
}
final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
.getSystemService(Context.CONNECTIVITY_SERVICE);
- mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
- new NetworkInfo(mInitialConfiguration.info),
- mInitialConfiguration.properties, mInitialConfiguration.capabilities,
- mInitialConfiguration.localNetworkConfig, mInitialConfiguration.score,
- mInitialConfiguration.config, providerId);
+ if (mInitialConfiguration.localNetworkConfig == null) {
+ // Call registerNetworkAgent without localNetworkConfig argument to pass
+ // android.net.cts.NetworkAgentTest#testAgentStartsInConnecting in old cts
+ mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+ new NetworkInfo(mInitialConfiguration.info),
+ mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+ mInitialConfiguration.score, mInitialConfiguration.config, providerId);
+ } else {
+ mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+ new NetworkInfo(mInitialConfiguration.info),
+ mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+ mInitialConfiguration.localNetworkConfig, mInitialConfiguration.score,
+ mInitialConfiguration.config, providerId);
+ }
mInitialConfiguration = null; // All this memory can now be GC'd
}
return mNetwork;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index ee5f25b..2640332 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.net.ConnectivityManager.NETID_UNSET;
@@ -90,6 +91,7 @@
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.ExecutorProvider;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
+import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
import com.android.server.connectivity.mdns.MdnsFeatureFlags;
import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
@@ -849,7 +851,9 @@
// service type would generate service instance names like
// Name._subtype._sub._type._tcp, which is incorrect
// (it should be Name._type._tcp).
- mAdvertiser.addService(transactionId, serviceInfo, typeSubtype.second);
+ mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
+ typeSubtype.second,
+ MdnsAdvertisingOptions.newBuilder().build());
storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
serviceInfo.getNetwork());
} else {
@@ -2104,11 +2108,17 @@
if (!SdkLevel.isAtLeastT()) {
throw new SecurityException("API is not available in before API level 33");
}
- // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V, but may
- // be back ported to older builds: accept it as long as it's signature-protected
- if (PermissionUtils.checkAnyPermissionOf(context, REGISTER_NSD_OFFLOAD_ENGINE)
- && (SdkLevel.isAtLeastV() || PermissionUtils.isSystemSignaturePermission(
- context, REGISTER_NSD_OFFLOAD_ENGINE))) {
+
+ // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V.
+ if (SdkLevel.isAtLeastV() && PermissionUtils.checkAnyPermissionOf(context,
+ REGISTER_NSD_OFFLOAD_ENGINE)) {
+ return;
+ }
+
+ // REGISTER_NSD_OFFLOAD_ENGINE cannot be backport to U. In U, check the DEVICE_POWER
+ // permission instead.
+ if (!SdkLevel.isAtLeastV() && SdkLevel.isAtLeastU()
+ && PermissionUtils.checkAnyPermissionOf(context, DEVICE_POWER)) {
return;
}
if (PermissionUtils.checkAnyPermissionOf(context, NETWORK_STACK,
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 1582fb6..c4d3338 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -32,6 +32,7 @@
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
@@ -122,17 +123,22 @@
return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
- int numQuestions = 0;
+ final List<MdnsRecord> questions = new ArrayList<>();
if (sendDiscoveryQueries) {
- numQuestions++; // Base service type
- if (!subtypes.isEmpty()) {
- numQuestions += subtypes.size();
+ // Base service type
+ questions.add(new MdnsPointerRecord(serviceTypeLabels, expectUnicastResponse));
+ for (String subtype : subtypes) {
+ final String[] labels = new String[serviceTypeLabels.length + 2];
+ labels[0] = MdnsConstants.SUBTYPE_PREFIX + subtype;
+ labels[1] = MdnsConstants.SUBTYPE_LABEL;
+ System.arraycopy(serviceTypeLabels, 0, labels, 2, serviceTypeLabels.length);
+
+ questions.add(new MdnsPointerRecord(labels, expectUnicastResponse));
}
}
// List of (name, type) to query
- final ArrayList<Pair<String[], Integer>> missingKnownAnswerRecords = new ArrayList<>();
final long now = clock.elapsedRealtime();
for (MdnsResponse response : servicesToResolve) {
final String[] serviceName = response.getServiceName();
@@ -142,13 +148,13 @@
boolean renewSrv = !response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded(
response.getServiceRecord(), now);
if (renewSrv && renewTxt) {
- missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_ANY));
+ questions.add(new MdnsAnyRecord(serviceName, expectUnicastResponse));
} else {
if (renewTxt) {
- missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_TXT));
+ questions.add(new MdnsTextRecord(serviceName, expectUnicastResponse));
}
if (renewSrv) {
- missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV));
+ questions.add(new MdnsServiceRecord(serviceName, expectUnicastResponse));
// The hostname is not yet known, so queries for address records will be
// sent the next time the EnqueueMdnsQueryCallable is enqueued if the reply
// does not contain them. In practice, advertisers should include the
@@ -157,46 +163,27 @@
} else if (!response.hasInet4AddressRecord()
&& !response.hasInet6AddressRecord()) {
final String[] host = response.getServiceRecord().getServiceHost();
- missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_A));
- missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_AAAA));
+ questions.add(new MdnsInetAddressRecord(
+ host, MdnsRecord.TYPE_A, expectUnicastResponse));
+ questions.add(new MdnsInetAddressRecord(
+ host, MdnsRecord.TYPE_AAAA, expectUnicastResponse));
}
}
}
- numQuestions += missingKnownAnswerRecords.size();
- if (numQuestions == 0) {
+ if (questions.size() == 0) {
// No query to send
return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
- // Header.
- packetWriter.writeUInt16(transactionId); // transaction ID
- packetWriter.writeUInt16(MdnsConstants.FLAGS_QUERY); // flags
- packetWriter.writeUInt16(numQuestions); // number of questions
- packetWriter.writeUInt16(0); // number of answers (not yet known; will be written later)
- packetWriter.writeUInt16(0); // number of authority entries
- packetWriter.writeUInt16(0); // number of additional records
-
- // Question(s) for missing records on known answers
- for (Pair<String[], Integer> question : missingKnownAnswerRecords) {
- writeQuestion(question.first, question.second);
- }
-
- // Question(s) for discovering other services with the type. There will be one question
- // for each (fqdn+subtype, recordType) combination, as well as one for each (fqdn,
- // recordType) combination.
- if (sendDiscoveryQueries) {
- for (String subtype : subtypes) {
- String[] labels = new String[serviceTypeLabels.length + 2];
- labels[0] = MdnsConstants.SUBTYPE_PREFIX + subtype;
- labels[1] = MdnsConstants.SUBTYPE_LABEL;
- System.arraycopy(serviceTypeLabels, 0, labels, 2, serviceTypeLabels.length);
-
- writeQuestion(labels, MdnsRecord.TYPE_PTR);
- }
- writeQuestion(serviceTypeLabels, MdnsRecord.TYPE_PTR);
- }
-
+ final MdnsPacket queryPacket = new MdnsPacket(
+ transactionId,
+ MdnsConstants.FLAGS_QUERY,
+ questions,
+ Collections.emptyList(), /* answers */
+ Collections.emptyList(), /* authorityRecords */
+ Collections.emptyList() /* additionalRecords */);
+ MdnsUtils.writeMdnsPacket(packetWriter, queryPacket);
sendPacketToIpv4AndIpv6(requestSender, MdnsConstants.MDNS_PORT);
for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
sendPacketToIpv4AndIpv6(requestSender, emulatorPort);
@@ -209,14 +196,6 @@
}
}
- private void writeQuestion(String[] labels, int type) throws IOException {
- packetWriter.writeLabels(labels);
- packetWriter.writeUInt16(type);
- packetWriter.writeUInt16(
- MdnsConstants.QCLASS_INTERNET
- | (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0));
- }
-
private void sendPacket(MdnsSocketClientBase requestSender, InetSocketAddress address)
throws IOException {
DatagramPacket packet = packetWriter.getPacket(address);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index 28e3924..fc0e11b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -43,6 +43,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -342,16 +343,16 @@
}
/**
- * Add a service.
+ * Add a service to advertise.
*
* Conflicts must be checked via {@link #getConflictingService} before attempting to add.
*/
- void addService(int id, Registration registration) {
+ void addService(int id, @NonNull Registration registration) {
mPendingRegistrations.put(id, registration);
for (int i = 0; i < mAdvertisers.size(); i++) {
try {
- mAdvertisers.valueAt(i).addService(
- id, registration.getServiceInfo(), registration.getSubtype());
+ mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo(),
+ registration.getSubtype());
} catch (NameConflictException e) {
mSharedLog.wtf("Name conflict adding services that should have unique names",
e);
@@ -359,6 +360,17 @@
}
}
+ /**
+ * Update an already registered service.
+ * The caller is expected to check that the service being updated doesn't change its name
+ */
+ void updateService(int id, @NonNull Registration registration) {
+ mPendingRegistrations.put(id, registration);
+ for (int i = 0; i < mAdvertisers.size(); i++) {
+ mAdvertisers.valueAt(i).updateService(id, registration.getSubtype());
+ }
+ }
+
void removeService(int id) {
mPendingRegistrations.remove(id);
for (int i = 0; i < mAdvertisers.size(); i++) {
@@ -474,7 +486,8 @@
@NonNull
private NsdServiceInfo mServiceInfo;
@Nullable
- private final String mSubtype;
+ private String mSubtype;
+
int mConflictDuringProbingCount;
int mConflictAfterProbingCount;
@@ -485,6 +498,22 @@
}
/**
+ * Matches between the NsdServiceInfo in the Registration and the provided argument.
+ */
+ public boolean matches(@Nullable NsdServiceInfo newInfo) {
+ return Objects.equals(newInfo.getServiceName(), mOriginalName) && Objects.equals(
+ newInfo.getServiceType(), mServiceInfo.getServiceType()) && Objects.equals(
+ newInfo.getNetwork(), mServiceInfo.getNetwork());
+ }
+
+ /**
+ * Update subType for the registration.
+ */
+ public void updateSubtype(@Nullable String subtype) {
+ this.mSubtype = subtype;
+ }
+
+ /**
* Update the registration to use a different service name, after a conflict was found.
*
* @param newInfo New service info to use.
@@ -632,42 +661,68 @@
}
/**
- * Add a service to advertise.
+ * Add or update a service to advertise.
+ *
* @param id A unique ID for the service.
* @param service The service info to advertise.
* @param subtype An optional subtype to advertise the service with.
+ * @param advertisingOptions The advertising options.
*/
- public void addService(int id, NsdServiceInfo service, @Nullable String subtype) {
+ public void addOrUpdateService(int id, NsdServiceInfo service, @Nullable String subtype,
+ MdnsAdvertisingOptions advertisingOptions) {
checkThread();
- if (mRegistrations.get(id) != null) {
- mSharedLog.e("Adding duplicate registration for " + service);
- // TODO (b/264986328): add a more specific error code
- mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
- return;
- }
-
- mSharedLog.i("Adding service " + service + " with ID " + id + " and subtype " + subtype);
-
+ final Registration existingRegistration = mRegistrations.get(id);
final Network network = service.getNetwork();
- final Registration registration = new Registration(service, subtype);
- final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
- if (network == null) {
- // If registering on all networks, no advertiser must have conflicts
- checkConflictFilter = (net, adv) -> true;
- } else {
- // If registering on one network, the matching network advertiser and the one for all
- // networks must not have conflicts
- checkConflictFilter = (net, adv) -> net == null || network.equals(net);
- }
+ Registration registration;
+ if (advertisingOptions.isOnlyUpdate()) {
+ if (existingRegistration == null) {
+ mSharedLog.e("Update non existing registration for " + service);
+ mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
+ return;
+ }
+ if (!(existingRegistration.matches(service))) {
+ mSharedLog.e("Update request can only update subType, serviceInfo: " + service
+ + ", existing serviceInfo: " + existingRegistration.getServiceInfo());
+ mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
+ return;
- updateRegistrationUntilNoConflict(checkConflictFilter, registration);
+ }
+ mSharedLog.i("Update service " + service + " with ID " + id + " and subtype " + subtype
+ + " advertisingOptions " + advertisingOptions);
+ registration = existingRegistration;
+ registration.updateSubtype(subtype);
+ } else {
+ if (existingRegistration != null) {
+ mSharedLog.e("Adding duplicate registration for " + service);
+ // TODO (b/264986328): add a more specific error code
+ mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
+ return;
+ }
+ mSharedLog.i("Adding service " + service + " with ID " + id + " and subtype " + subtype
+ + " advertisingOptions " + advertisingOptions);
+ registration = new Registration(service, subtype);
+ final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
+ if (network == null) {
+ // If registering on all networks, no advertiser must have conflicts
+ checkConflictFilter = (net, adv) -> true;
+ } else {
+ // If registering on one network, the matching network advertiser and the one
+ // for all networks must not have conflicts
+ checkConflictFilter = (net, adv) -> net == null || network.equals(net);
+ }
+ updateRegistrationUntilNoConflict(checkConflictFilter, registration);
+ }
InterfaceAdvertiserRequest advertiser = mAdvertiserRequests.get(network);
if (advertiser == null) {
advertiser = new InterfaceAdvertiserRequest(network);
mAdvertiserRequests.put(network, advertiser);
}
- advertiser.addService(id, registration);
+ if (advertisingOptions.isOnlyUpdate()) {
+ advertiser.updateService(id, registration);
+ } else {
+ advertiser.addService(id, registration);
+ }
mRegistrations.put(id, registration);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertisingOptions.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertisingOptions.java
new file mode 100644
index 0000000..e7a6ca7
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertisingOptions.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+/**
+ * API configuration parameters for advertising the mDNS service.
+ *
+ * <p>Use {@link MdnsAdvertisingOptions.Builder} to create {@link MdnsAdvertisingOptions}.
+ *
+ * @hide
+ */
+public class MdnsAdvertisingOptions {
+
+ private static MdnsAdvertisingOptions sDefaultOptions;
+ private final boolean mIsOnlyUpdate;
+
+ /**
+ * Parcelable constructs for a {@link MdnsAdvertisingOptions}.
+ */
+ MdnsAdvertisingOptions(
+ boolean isOnlyUpdate) {
+ this.mIsOnlyUpdate = isOnlyUpdate;
+ }
+
+ /**
+ * Returns a {@link Builder} for {@link MdnsAdvertisingOptions}.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns a default search options.
+ */
+ public static synchronized MdnsAdvertisingOptions getDefaultOptions() {
+ if (sDefaultOptions == null) {
+ sDefaultOptions = newBuilder().build();
+ }
+ return sDefaultOptions;
+ }
+
+ /**
+ * @return {@code true} if the advertising request is an update request.
+ */
+ public boolean isOnlyUpdate() {
+ return mIsOnlyUpdate;
+ }
+
+ @Override
+ public String toString() {
+ return "MdnsAdvertisingOptions{" + "mIsOnlyUpdate=" + mIsOnlyUpdate + '}';
+ }
+
+ /**
+ * A builder to create {@link MdnsAdvertisingOptions}.
+ */
+ public static final class Builder {
+ private boolean mIsOnlyUpdate = false;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets if the advertising request is an update request.
+ */
+ public Builder setIsOnlyUpdate(boolean isOnlyUpdate) {
+ this.mIsOnlyUpdate = isOnlyUpdate;
+ return this;
+ }
+
+ /**
+ * Builds a {@link MdnsAdvertisingOptions} with the arguments supplied to this builder.
+ */
+ public MdnsAdvertisingOptions build() {
+ return new MdnsAdvertisingOptions(mIsOnlyUpdate);
+ }
+ }
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
index 973fd96..4399f2d 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
@@ -60,6 +60,12 @@
super(name, type, reader, isQuestion);
}
+ public MdnsInetAddressRecord(String[] name, int type, boolean isUnicast) {
+ super(name, type,
+ MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0),
+ 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */);
+ }
+
public MdnsInetAddressRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
long ttlMillis, InetAddress address) {
super(name, address instanceof Inet4Address ? TYPE_A : TYPE_AAAA,
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 62c37ad..463df63 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -229,6 +229,18 @@
}
/**
+ * Update an already registered service without sending exit/re-announcement packet.
+ *
+ * @param id An exiting service id
+ * @param subtype A new subtype
+ */
+ public void updateService(int id, @Nullable String subtype) {
+ // The current implementation is intended to be used in cases where subtypes don't get
+ // announced.
+ mRecordRepository.updateService(id, subtype);
+ }
+
+ /**
* Start advertising a service.
*
* @throws NameConflictException There is already a service being advertised with that name.
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
index 41cc380..e5c90a4 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
@@ -39,6 +39,12 @@
super(name, TYPE_PTR, reader, isQuestion);
}
+ public MdnsPointerRecord(String[] name, boolean isUnicast) {
+ super(name, TYPE_PTR,
+ MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0),
+ 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */);
+ }
+
public MdnsPointerRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
long ttlMillis, String[] pointer) {
super(name, TYPE_PTR, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index e34778f..48ece68 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -167,7 +167,7 @@
/**
* Whether the service is sending exit announcements and will be destroyed soon.
*/
- public boolean exiting = false;
+ public boolean exiting;
/**
* The replied query packet count of this service.
@@ -185,13 +185,20 @@
private boolean isProbing;
/**
+ * Create a ServiceRegistration with only update the subType
+ */
+ ServiceRegistration withSubtype(String newSubType) {
+ return new ServiceRegistration(srvRecord.record.getServiceHost(), serviceInfo,
+ newSubType, repliedServiceCount, sentPacketCount, exiting, isProbing);
+ }
+
+
+ /**
* Create a ServiceRegistration for dns-sd service registration (RFC6763).
- *
- * @param deviceHostname Hostname of the device (for the interface used)
- * @param serviceInfo Service to advertise
*/
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
- @Nullable String subtype, int repliedServiceCount, int sentPacketCount) {
+ @Nullable String subtype, int repliedServiceCount, int sentPacketCount,
+ boolean exiting, boolean isProbing) {
this.serviceInfo = serviceInfo;
this.subtype = subtype;
@@ -266,7 +273,20 @@
this.allRecords = Collections.unmodifiableList(allRecords);
this.repliedServiceCount = repliedServiceCount;
this.sentPacketCount = sentPacketCount;
- this.isProbing = true;
+ this.isProbing = isProbing;
+ this.exiting = exiting;
+ }
+
+ /**
+ * Create a ServiceRegistration for dns-sd service registration (RFC6763).
+ *
+ * @param deviceHostname Hostname of the device (for the interface used)
+ * @param serviceInfo Service to advertise
+ */
+ ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
+ @Nullable String subtype, int repliedServiceCount, int sentPacketCount) {
+ this(deviceHostname, serviceInfo, subtype, repliedServiceCount, sentPacketCount,
+ false /* exiting */, true /* isProbing */);
}
void setProbing(boolean probing) {
@@ -305,6 +325,24 @@
}
/**
+ * Update a service that already registered in the repository.
+ *
+ * @param serviceId An existing service ID.
+ * @param subtype A new subtype
+ * @return
+ */
+ public void updateService(int serviceId, @Nullable String subtype) {
+ final ServiceRegistration existingRegistration = mServices.get(serviceId);
+ if (existingRegistration == null) {
+ throw new IllegalArgumentException(
+ "Service ID must already exist for an update request: " + serviceId);
+ }
+ final ServiceRegistration updatedRegistration = existingRegistration.withSubtype(
+ subtype);
+ mServices.put(serviceId, updatedRegistration);
+ }
+
+ /**
* Add a service to the repository.
*
* This may remove/replace any existing service that used the name added but is exiting.
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
index 4d407be..0d6a9ec 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
@@ -49,6 +49,12 @@
super(name, TYPE_SRV, reader, isQuestion);
}
+ public MdnsServiceRecord(String[] name, boolean isUnicast) {
+ super(name, TYPE_SRV,
+ MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0),
+ 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */);
+ }
+
public MdnsServiceRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
long ttlMillis, int servicePriority, int serviceWeight, int servicePort,
String[] serviceHost) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java
index cf6c8ac..92cf324 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java
@@ -42,6 +42,12 @@
super(name, TYPE_TXT, reader, isQuestion);
}
+ public MdnsTextRecord(String[] name, boolean isUnicast) {
+ super(name, TYPE_TXT,
+ MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0),
+ 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */);
+ }
+
public MdnsTextRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, long ttlMillis,
List<TextEntry> entries) {
super(name, TYPE_TXT, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
index 4d79f9d..1482ebb 100644
--- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -183,13 +183,10 @@
}
/**
- * Create a raw DNS packet.
+ * Write the mdns packet from given MdnsPacket.
*/
- public static byte[] createRawDnsPacket(@NonNull byte[] packetCreationBuffer,
- @NonNull MdnsPacket packet) throws IOException {
- // TODO: support packets over size (send in multiple packets with TC bit set)
- final MdnsPacketWriter writer = new MdnsPacketWriter(packetCreationBuffer);
-
+ public static void writeMdnsPacket(@NonNull MdnsPacketWriter writer, @NonNull MdnsPacket packet)
+ throws IOException {
writer.writeUInt16(packet.transactionId); // Transaction ID (advertisement: 0)
writer.writeUInt16(packet.flags); // Response, authoritative (rfc6762 18.4)
writer.writeUInt16(packet.questions.size()); // questions count
@@ -210,6 +207,16 @@
for (MdnsRecord record : packet.additionalRecords) {
record.write(writer, 0L);
}
+ }
+
+ /**
+ * Create a raw DNS packet.
+ */
+ public static byte[] createRawDnsPacket(@NonNull byte[] packetCreationBuffer,
+ @NonNull MdnsPacket packet) throws IOException {
+ // TODO: support packets over size (send in multiple packets with TC bit set)
+ final MdnsPacketWriter writer = new MdnsPacketWriter(packetCreationBuffer);
+ writeMdnsPacket(writer, packet);
final int len = writer.getWritePosition();
return Arrays.copyOfRange(packetCreationBuffer, 0, len);
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 01b8de7..48e86d8 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -48,7 +48,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.SharedLog;
@@ -238,18 +237,7 @@
mDeps = deps;
// Interface match regex.
- String ifaceMatchRegex = mDeps.getInterfaceRegexFromResource(mContext);
- // "*" is a magic string to indicate "pick the default".
- if (ifaceMatchRegex.equals("*")) {
- if (SdkLevel.isAtLeastU()) {
- // On U+, include both usb%d and eth%d interfaces.
- ifaceMatchRegex = "(usb|eth)\\d+";
- } else {
- // On T, include only eth%d interfaces.
- ifaceMatchRegex = "eth\\d+";
- }
- }
- mIfaceMatch = ifaceMatchRegex;
+ mIfaceMatch = mDeps.getInterfaceRegexFromResource(mContext);
// Read default Ethernet interface configuration from resources
final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context);
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 045d707f..f30abc6 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -194,11 +194,8 @@
-->
</string-array>
- <!-- Regex of wired ethernet ifaces. Network interfaces that match this regex will be tracked
- by ethernet service.
- If set to "*", ethernet service uses "(eth|usb)\\d+" on Android U+ and eth\\d+ on
- Android T. -->
- <string translatable="false" name="config_ethernet_iface_regex">*</string>
+ <!-- Regex of wired ethernet ifaces -->
+ <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
<!-- Ignores Wi-Fi validation failures after roam.
If validation fails on a Wi-Fi network after a roam to a new BSSID,
diff --git a/service/src/com/android/server/BpfLoaderRcUtils.java b/service/src/com/android/server/BpfLoaderRcUtils.java
new file mode 100644
index 0000000..1b6ee55
--- /dev/null
+++ b/service/src/com/android/server/BpfLoaderRcUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BpfRcUtils is responsible for comparing the bpf loader rc file.
+ *
+ * {@hide}
+ */
+public class BpfLoaderRcUtils {
+ public static final String TAG = BpfLoaderRcUtils.class.getSimpleName();
+
+ private static final List<String> BPF_LOADER_RC_S_T = List.of(
+ "service bpfloader /system/bin/bpfloader",
+ "capabilities CHOWN SYS_ADMIN NET_ADMIN",
+ "rlimit memlock 1073741824 1073741824",
+ "oneshot",
+ "reboot_on_failure reboot,bpfloader-failed",
+ "updatable"
+ );
+
+ private static final List<String> BPF_LOADER_RC_U = List.of(
+ "service bpfloader /system/bin/bpfloader",
+ "capabilities CHOWN SYS_ADMIN NET_ADMIN",
+ "group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system",
+ "user root",
+ "rlimit memlock 1073741824 1073741824",
+ "oneshot",
+ "reboot_on_failure reboot,bpfloader-failed",
+ "updatable"
+ );
+
+ private static final List<String> BPF_LOADER_RC_UQPR2 = List.of(
+ "service bpfloader /system/bin/netbpfload",
+ "capabilities CHOWN SYS_ADMIN NET_ADMIN",
+ "group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system",
+ "user root",
+ "rlimit memlock 1073741824 1073741824",
+ "oneshot",
+ "reboot_on_failure reboot,bpfloader-failed",
+ "updatable"
+ );
+
+
+ private static final String BPF_LOADER_RC_FILE_PATH = "/etc/init/bpfloader.rc";
+ private static final String NET_BPF_LOAD_RC_FILE_PATH = "/etc/init/netbpfload.rc";
+
+ private BpfLoaderRcUtils() {
+ }
+
+ /**
+ * Load the bpf rc file content from the input stream.
+ */
+ @VisibleForTesting
+ public static List<String> loadExistingBpfRcFile(@NonNull InputStream inputStream) {
+ List<String> contents = new ArrayList<>();
+ boolean bpfSectionFound = false;
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+ if (line.startsWith("#")) {
+ continue;
+ }
+ // If bpf service section was found and new service or action section start. The
+ // read should stop.
+ if (bpfSectionFound && (line.startsWith("service ") || (line.startsWith("on ")))) {
+ break;
+ }
+ if (line.startsWith("service bpfloader ")) {
+ bpfSectionFound = true;
+ }
+ if (bpfSectionFound) {
+ contents.add(line);
+ }
+ }
+ } catch (IOException e) {
+ Log.wtf("read input stream failed.", e);
+ contents.clear();
+ return contents;
+ }
+ return contents;
+ }
+
+ /**
+ * Check the bpfLoader rc file on the system image matches any of the template files.
+ */
+ public static boolean checkBpfLoaderRc() {
+ File bpfRcFile = new File(BPF_LOADER_RC_FILE_PATH);
+ if (!bpfRcFile.exists()) {
+ if (SdkLevel.isAtLeastU()) {
+ bpfRcFile = new File(NET_BPF_LOAD_RC_FILE_PATH);
+ }
+ if (!bpfRcFile.exists()) {
+ Log.wtf(TAG,
+ "neither " + BPF_LOADER_RC_FILE_PATH + " nor " + NET_BPF_LOAD_RC_FILE_PATH
+ + " exist.");
+ return false;
+ }
+ // Check bpf rc file in U QPR2
+ return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_UQPR2);
+ }
+
+ if (SdkLevel.isAtLeastU()) {
+ // Check bpf rc file in U
+ return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_U);
+ }
+ // Check bpf rc file in S/T
+ return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_S_T);
+ }
+
+ private static boolean compareBpfLoaderRc(@NonNull File bpfRcFile,
+ @NonNull List<String> template) {
+ try {
+ List<String> actualContent = loadExistingBpfRcFile(new FileInputStream(bpfRcFile));
+ if (!actualContent.equals(template)) {
+ Log.wtf(TAG, "BPF rc file is not same as the template files " + actualContent);
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ Log.wtf(bpfRcFile.getPath() + " doesn't exist.", e);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8f29078..9268da5 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -1876,6 +1876,10 @@
activityManager.registerUidFrozenStateChangedCallback(
(Runnable r) -> r.run(), frozenStateChangedCallback);
}
+
+ if (mDeps.isFeatureNotChickenedOut(mContext, LOG_BPF_RC)) {
+ mHandler.post(BpfLoaderRcUtils::checkBpfLoaderRc);
+ }
}
/**
@@ -3335,6 +3339,8 @@
public static final String ALLOW_SYSUI_CONNECTIVITY_REPORTS =
"allow_sysui_connectivity_reports";
+ public static final String LOG_BPF_RC = "log_bpf_rc_force_disable";
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -4784,7 +4790,7 @@
// If the Private DNS mode is opportunistic, reprogram the DNS servers
// in order to restart a validation pass from within netd.
final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
- if (cfg.useTls && TextUtils.isEmpty(cfg.hostname)) {
+ if (cfg.inOpportunisticMode()) {
updateDnses(nai.linkProperties, null, nai.network.getNetId());
}
}
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index 894bcc4..8e6854a 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -302,7 +302,7 @@
final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
PRIVATE_DNS_OFF);
- final boolean useTls = privateDnsCfg.useTls;
+ final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF;
final PrivateDnsValidationStatuses statuses =
useTls ? mPrivateDnsValidationMap.get(netId) : null;
final boolean validated = (null != statuses) && statuses.hasValidatedServer();
@@ -370,7 +370,7 @@
// networks like IMS.
final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
PRIVATE_DNS_OFF);
- final boolean useTls = privateDnsCfg.useTls;
+ final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF;
final boolean strictMode = privateDnsCfg.inStrictMode();
paramsParcel.netId = netId;
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java
new file mode 100644
index 0000000..bdcdcd8
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.net.InetAddress;
+
+/**
+ * Struct xfrm_id
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_id {
+ * xfrm_address_t daddr;
+ * __be32 spi;
+ * __u8 proto;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmId extends Struct {
+ public static final int STRUCT_SIZE = 24;
+
+ @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+ public final byte[] nestedStructDAddr;
+
+ @Field(order = 1, type = Type.UBE32)
+ public final long spi;
+
+ @Field(order = 2, type = Type.U8, padding = 3)
+ public final short proto;
+
+ @Computed private final StructXfrmAddressT mDestXfrmAddressT;
+
+ // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+ public StructXfrmId(@NonNull final byte[] nestedStructDAddr, long spi, short proto) {
+ this.nestedStructDAddr = nestedStructDAddr.clone();
+ this.spi = spi;
+ this.proto = proto;
+
+ mDestXfrmAddressT = new StructXfrmAddressT(this.nestedStructDAddr);
+ }
+
+ // Constructor to build a new message
+ public StructXfrmId(@NonNull final InetAddress destAddress, long spi, short proto) {
+ this(new StructXfrmAddressT(destAddress).writeToBytes(), spi, proto);
+ }
+
+ /** Return the destination address */
+ public InetAddress getDestAddress(int family) {
+ return mDestXfrmAddressT.getAddress(family);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java
new file mode 100644
index 0000000..12f68c8
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_INF;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.math.BigInteger;
+
+/**
+ * Struct xfrm_lifetime_cfg
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_lifetime_cfg {
+ * __u64 soft_byte_limit;
+ * __u64 hard_byte_limit;
+ * __u64 soft_packet_limit;
+ * __u64 hard_packet_limit;
+ * __u64 soft_add_expires_seconds;
+ * __u64 hard_add_expires_seconds;
+ * __u64 soft_use_expires_seconds;
+ * __u64 hard_use_expires_seconds;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmLifetimeCfg extends Struct {
+ public static final int STRUCT_SIZE = 64;
+
+ @Field(order = 0, type = Type.U64)
+ public final BigInteger softByteLimit;
+
+ @Field(order = 1, type = Type.U64)
+ public final BigInteger hardByteLimit;
+
+ @Field(order = 2, type = Type.U64)
+ public final BigInteger softPacketLimit;
+
+ @Field(order = 3, type = Type.U64)
+ public final BigInteger hardPacketLimit;
+
+ @Field(order = 4, type = Type.U64)
+ public final BigInteger softAddExpiresSeconds;
+
+ @Field(order = 5, type = Type.U64)
+ public final BigInteger hardAddExpiresSeconds;
+
+ @Field(order = 6, type = Type.U64)
+ public final BigInteger softUseExpiresSeconds;
+
+ @Field(order = 7, type = Type.U64)
+ public final BigInteger hardUseExpiresSeconds;
+
+ // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+ public StructXfrmLifetimeCfg(
+ @NonNull final BigInteger softByteLimit,
+ @NonNull final BigInteger hardByteLimit,
+ @NonNull final BigInteger softPacketLimit,
+ @NonNull final BigInteger hardPacketLimit,
+ @NonNull final BigInteger softAddExpiresSeconds,
+ @NonNull final BigInteger hardAddExpiresSeconds,
+ @NonNull final BigInteger softUseExpiresSeconds,
+ @NonNull final BigInteger hardUseExpiresSeconds) {
+ this.softByteLimit = softByteLimit;
+ this.hardByteLimit = hardByteLimit;
+ this.softPacketLimit = softPacketLimit;
+ this.hardPacketLimit = hardPacketLimit;
+ this.softAddExpiresSeconds = softAddExpiresSeconds;
+ this.hardAddExpiresSeconds = hardAddExpiresSeconds;
+ this.softUseExpiresSeconds = softUseExpiresSeconds;
+ this.hardUseExpiresSeconds = hardUseExpiresSeconds;
+ }
+
+ // Constructor to build a new message
+ public StructXfrmLifetimeCfg() {
+ this(
+ XFRM_INF,
+ XFRM_INF,
+ XFRM_INF,
+ XFRM_INF,
+ BigInteger.ZERO,
+ BigInteger.ZERO,
+ BigInteger.ZERO,
+ BigInteger.ZERO);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java
new file mode 100644
index 0000000..6a539c7
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.math.BigInteger;
+
+/**
+ * Struct xfrm_lifetime_cur
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_lifetime_cur {
+ * __u64 bytes;
+ * __u64 packets;
+ * __u64 add_time;
+ * __u64 use_time;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmLifetimeCur extends Struct {
+ public static final int STRUCT_SIZE = 32;
+
+ @Field(order = 0, type = Type.U64)
+ public final BigInteger bytes;
+
+ @Field(order = 1, type = Type.U64)
+ public final BigInteger packets;
+
+ @Field(order = 2, type = Type.U64)
+ public final BigInteger addTime;
+
+ @Field(order = 3, type = Type.U64)
+ public final BigInteger useTime;
+
+ public StructXfrmLifetimeCur(
+ @NonNull final BigInteger bytes,
+ @NonNull final BigInteger packets,
+ @NonNull final BigInteger addTime,
+ @NonNull final BigInteger useTime) {
+ this.bytes = bytes;
+ this.packets = packets;
+ this.addTime = addTime;
+ this.useTime = useTime;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java
new file mode 100644
index 0000000..7bd2ca1
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Struct xfrm_selector
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_selector {
+ * xfrm_address_t daddr;
+ * xfrm_address_t saddr;
+ * __be16 dport;
+ * __be16 dport_mask;
+ * __be16 sport;
+ * __be16 sport_mask;
+ * __u16 family;
+ * __u8 prefixlen_d;
+ * __u8 prefixlen_s;
+ * __u8 proto;
+ * int ifindex;
+ * __kernel_uid32_t user;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmSelector extends Struct {
+ public static final int STRUCT_SIZE = 56;
+
+ @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+ public final byte[] nestedStructDAddr;
+
+ @Field(order = 1, type = Type.ByteArray, arraysize = 16)
+ public final byte[] nestedStructSAddr;
+
+ @Field(order = 2, type = Type.UBE16)
+ public final int dPort;
+
+ @Field(order = 3, type = Type.UBE16)
+ public final int dPortMask;
+
+ @Field(order = 4, type = Type.UBE16)
+ public final int sPort;
+
+ @Field(order = 5, type = Type.UBE16)
+ public final int sPortMask;
+
+ @Field(order = 6, type = Type.U16)
+ public final int selectorFamily;
+
+ @Field(order = 7, type = Type.U8, padding = 1)
+ public final short prefixlenD;
+
+ @Field(order = 8, type = Type.U8, padding = 1)
+ public final short prefixlenS;
+
+ @Field(order = 9, type = Type.U8, padding = 1)
+ public final short proto;
+
+ @Field(order = 10, type = Type.S32)
+ public final int ifIndex;
+
+ @Field(order = 11, type = Type.S32)
+ public final int user;
+
+ // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+ public StructXfrmSelector(
+ @NonNull final byte[] nestedStructDAddr,
+ @NonNull final byte[] nestedStructSAddr,
+ int dPort,
+ int dPortMask,
+ int sPort,
+ int sPortMask,
+ int selectorFamily,
+ short prefixlenD,
+ short prefixlenS,
+ short proto,
+ int ifIndex,
+ int user) {
+ this.nestedStructDAddr = nestedStructDAddr.clone();
+ this.nestedStructSAddr = nestedStructSAddr.clone();
+ this.dPort = dPort;
+ this.dPortMask = dPortMask;
+ this.sPort = sPort;
+ this.sPortMask = sPortMask;
+ this.selectorFamily = selectorFamily;
+ this.prefixlenD = prefixlenD;
+ this.prefixlenS = prefixlenS;
+ this.proto = proto;
+ this.ifIndex = ifIndex;
+ this.user = user;
+ }
+
+ // Constructor to build a new message
+ public StructXfrmSelector(int selectorFamily) {
+ this(
+ new byte[StructXfrmAddressT.STRUCT_SIZE],
+ new byte[StructXfrmAddressT.STRUCT_SIZE],
+ 0 /* dPort */,
+ 0 /* dPortMask */,
+ 0 /* sPort */,
+ 0 /* sPortMask */,
+ selectorFamily,
+ (short) 0 /* prefixlenD */,
+ (short) 0 /* prefixlenS */,
+ (short) 0 /* proto */,
+ 0 /* ifIndex */,
+ 0 /* user */);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
index e15342b..9773cd6 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
@@ -22,6 +22,7 @@
import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.StructNlMsgHdr;
+import java.math.BigInteger;
import java.nio.ByteBuffer;
/** Base calss for XFRM netlink messages */
@@ -40,6 +41,8 @@
public static final short XFRM_MSG_NEWSA = 16;
public static final short XFRM_MSG_GETSA = 18;
+ public static final BigInteger XFRM_INF = new BigInteger("FFFFFFFFFFFFFFFF", 16);
+
public XfrmNetlinkMessage(@NonNull final StructNlMsgHdr header) {
super(header);
}
diff --git a/staticlibs/framework/com/android/net/module/util/PermissionUtils.java b/staticlibs/framework/com/android/net/module/util/PermissionUtils.java
index d5b4c90..8315b8f 100644
--- a/staticlibs/framework/com/android/net/module/util/PermissionUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/PermissionUtils.java
@@ -21,15 +21,12 @@
import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.os.Binder;
import java.io.PrintWriter;
@@ -57,25 +54,6 @@
}
/**
- * Return true if the permission has system signature.
- */
- public static boolean isSystemSignaturePermission(@NonNull Context context,
- @NonNull String permission) {
- try {
- PermissionInfo permissionInfo = context.getPackageManager().getPermissionInfo(
- permission, 0 /* flags */);
- if (permissionInfo == null) {
- return false;
- }
- return "android".equals(permissionInfo.packageName)
- && permissionInfo.getProtection() == PROTECTION_SIGNATURE;
- } catch (PackageManager.NameNotFoundException ignored) {
- // Ignored the NameNotFoundException and return false
- }
- return false;
- }
-
- /**
* Return true if the context has one of give permission that is allowed
* for a particular process and user ID running in the system.
*/
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/PermissionUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/PermissionUtilsTest.kt
index 028308b..c5a91a4 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/PermissionUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/PermissionUtilsTest.kt
@@ -16,15 +16,12 @@
package com.android.net.module.util
-import android.Manifest.permission.INTERNET
-import android.Manifest.permission.NETWORK_SETTINGS
import android.Manifest.permission.NETWORK_STACK
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_DENIED
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
-import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.net.module.util.PermissionUtils.checkAnyPermissionOf
@@ -144,27 +141,4 @@
Assert.fail("Exception should have not been thrown with system feature enabled")
}
}
-
- @Test
- @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
- fun testIsSystemSignaturePermission() {
- assertTrue(
- PermissionUtils.isSystemSignaturePermission(
- context,
- NETWORK_SETTINGS
- )
- )
- assertFalse(
- PermissionUtils
- .isSystemSignaturePermission(context, PERMISSION_MAINLINE_NETWORK_STACK)
- )
- assertFalse(
- PermissionUtils
- .isSystemSignaturePermission(context, "test_permission")
- )
- assertFalse(
- PermissionUtils
- .isSystemSignaturePermission(context, INTERNET)
- )
- }
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java
new file mode 100644
index 0000000..c9741cf
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.IPPROTO_ESP;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmIdTest {
+ private static final String EXPECTED_HEX_STRING =
+ "C0000201000000000000000000000000" + "53FA0FDD32000000";
+ private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+ private static final InetAddress DEST_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
+ private static final long SPI = 0x53fa0fdd;
+ private static final short PROTO = IPPROTO_ESP;
+
+ @Test
+ public void testEncode() throws Exception {
+ final StructXfrmId struct = new StructXfrmId(DEST_ADDRESS, SPI, PROTO);
+
+ final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+ buffer.order(ByteOrder.nativeOrder());
+ struct.writeToByteBuffer(buffer);
+
+ assertArrayEquals(EXPECTED_HEX, buffer.array());
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+ buffer.order(ByteOrder.nativeOrder());
+ final StructXfrmId struct = StructXfrmId.parse(StructXfrmId.class, buffer);
+
+ assertEquals(DEST_ADDRESS, struct.getDestAddress(OsConstants.AF_INET));
+ assertEquals(SPI, struct.spi);
+ assertEquals(PROTO, struct.proto);
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java
new file mode 100644
index 0000000..69360f6
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_INF;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmLifetimeCfgTest {
+ private static final String EXPECTED_HEX_STRING =
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000";
+ private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+
+ @Test
+ public void testEncode() throws Exception {
+ final StructXfrmLifetimeCfg struct = new StructXfrmLifetimeCfg();
+
+ final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+ buffer.order(ByteOrder.nativeOrder());
+ struct.writeToByteBuffer(buffer);
+
+ assertArrayEquals(EXPECTED_HEX, buffer.array());
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+ buffer.order(ByteOrder.nativeOrder());
+ final StructXfrmLifetimeCfg struct =
+ StructXfrmLifetimeCfg.parse(StructXfrmLifetimeCfg.class, buffer);
+
+ assertEquals(XFRM_INF, struct.softByteLimit);
+ assertEquals(XFRM_INF, struct.hardByteLimit);
+ assertEquals(XFRM_INF, struct.softPacketLimit);
+ assertEquals(XFRM_INF, struct.hardPacketLimit);
+ assertEquals(BigInteger.ZERO, struct.softAddExpiresSeconds);
+ assertEquals(BigInteger.ZERO, struct.hardAddExpiresSeconds);
+ assertEquals(BigInteger.ZERO, struct.softUseExpiresSeconds);
+ assertEquals(BigInteger.ZERO, struct.hardUseExpiresSeconds);
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java
new file mode 100644
index 0000000..008c922
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmLifetimeCurTest {
+ private static final String EXPECTED_HEX_STRING =
+ "00000000000000000000000000000000" + "8CFE4265000000000000000000000000";
+ private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+ private static final BigInteger ADD_TIME;
+
+ static {
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ cal.set(2023, Calendar.NOVEMBER, 2, 1, 42, 36);
+ final long timestampSeconds = TimeUnit.MILLISECONDS.toSeconds(cal.getTimeInMillis());
+ ADD_TIME = BigInteger.valueOf(timestampSeconds);
+ }
+
+ @Test
+ public void testEncode() throws Exception {
+ final StructXfrmLifetimeCur struct =
+ new StructXfrmLifetimeCur(
+ BigInteger.ZERO, BigInteger.ZERO, ADD_TIME, BigInteger.ZERO);
+
+ final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+ buffer.order(ByteOrder.nativeOrder());
+ struct.writeToByteBuffer(buffer);
+
+ assertArrayEquals(EXPECTED_HEX, buffer.array());
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+ buffer.order(ByteOrder.nativeOrder());
+ final StructXfrmLifetimeCur struct =
+ StructXfrmLifetimeCur.parse(StructXfrmLifetimeCur.class, buffer);
+
+ assertEquals(BigInteger.ZERO, struct.bytes);
+ assertEquals(BigInteger.ZERO, struct.packets);
+ assertEquals(ADD_TIME, struct.addTime);
+ assertEquals(BigInteger.ZERO, struct.useTime);
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java
new file mode 100644
index 0000000..99f3b2a
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmSelectorTest {
+ private static final String EXPECTED_HEX_STRING =
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000200000000000000"
+ + "0000000000000000";
+ private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+
+ private static final byte[] XFRM_ADDRESS_T_ANY_BYTES = new byte[16];
+ private static final int FAMILY = OsConstants.AF_INET;
+
+ @Test
+ public void testEncode() throws Exception {
+ final StructXfrmSelector struct = new StructXfrmSelector(FAMILY);
+
+ final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+ buffer.order(ByteOrder.nativeOrder());
+ struct.writeToByteBuffer(buffer);
+
+ assertArrayEquals(EXPECTED_HEX, buffer.array());
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+ buffer.order(ByteOrder.nativeOrder());
+ final StructXfrmSelector struct =
+ StructXfrmSelector.parse(StructXfrmSelector.class, buffer);
+
+ assertArrayEquals(XFRM_ADDRESS_T_ANY_BYTES, struct.nestedStructDAddr);
+ assertArrayEquals(XFRM_ADDRESS_T_ANY_BYTES, struct.nestedStructSAddr);
+ assertEquals(0, struct.dPort);
+ assertEquals(0, struct.dPortMask);
+ assertEquals(0, struct.sPort);
+ assertEquals(0, struct.sPortMask);
+ assertEquals(FAMILY, struct.selectorFamily);
+ assertEquals(0, struct.prefixlenD);
+ assertEquals(0, struct.prefixlenS);
+ assertEquals(0, struct.proto);
+ assertEquals(0, struct.ifIndex);
+ assertEquals(0, struct.user);
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 225408c..c7d6555 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -131,17 +131,6 @@
import com.android.testutils.assertThrows
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.argThat
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.timeout
-import org.mockito.Mockito.verify
import java.io.Closeable
import java.io.IOException
import java.net.DatagramSocket
@@ -160,6 +149,17 @@
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
private const val TAG = "NetworkAgentTest"
// This test doesn't really have a constraint on how fast the methods should return. If it's
@@ -951,7 +951,6 @@
argThat<NetworkInfo> { it.detailedState == NetworkInfo.DetailedState.CONNECTING },
any(LinkProperties::class.java),
any(NetworkCapabilities::class.java),
- any(), // LocalNetworkConfig TODO : specify when it's public
any(NetworkScore::class.java),
any(NetworkAgentConfig::class.java),
eq(NetworkProvider.ID_NONE))
diff --git a/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt b/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt
new file mode 100644
index 0000000..2cf6b17
--- /dev/null
+++ b/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 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.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.S)
+class BpfLoaderRcUtilsTest {
+ @Test
+ fun testLoadExistingBpfRcFile() {
+
+ val inputString = """
+ service a
+ # test comment
+ service bpfloader /system/bin/bpfloader
+ capabilities CHOWN SYS_ADMIN NET_ADMIN
+ group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system
+ user root
+ rlimit memlock 1073741824 1073741824
+ oneshot
+ # comment 漢字
+ reboot_on_failure reboot,bpfloader-failed
+ updatable
+
+ #test comment
+ on b
+ oneshot
+ # test comment
+ """.trimIndent()
+ val expectedResult = listOf(
+ "service bpfloader /system/bin/bpfloader",
+ "capabilities CHOWN SYS_ADMIN NET_ADMIN",
+ "group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system",
+ "user root",
+ "rlimit memlock 1073741824 1073741824",
+ "oneshot",
+ "reboot_on_failure reboot,bpfloader-failed",
+ "updatable"
+ )
+
+ assertEquals(expectedResult,
+ BpfLoaderRcUtils.loadExistingBpfRcFile(inputString.byteInputStream()))
+ }
+
+ @Test
+ fun testCheckBpfRcFile() {
+ assertTrue(BpfLoaderRcUtils.checkBpfLoaderRc())
+ }
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 9fad766..8f5fd7c 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -161,6 +161,7 @@
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
import static com.android.server.ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS;
import static com.android.server.ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION;
+import static com.android.server.ConnectivityService.LOG_BPF_RC;
import static com.android.server.ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM;
@@ -2157,6 +2158,8 @@
switch (name) {
case ALLOW_SYSUI_CONNECTIVITY_REPORTS:
return true;
+ case LOG_BPF_RC:
+ return true;
default:
return super.isFeatureNotChickenedOut(context, name);
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index ffc8aa1..32014c2 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
@@ -24,7 +25,6 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -75,7 +75,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.net.INetd;
import android.net.Network;
import android.net.mdns.aidl.DiscoveryInfo;
@@ -170,6 +169,8 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public TestRule ignoreRule = new DevSdkIgnoreRule();
@Mock Context mContext;
@Mock PackageManager mPackageManager;
@Mock ContentResolver mResolver;
@@ -1114,9 +1115,9 @@
final RegistrationListener regListener = mock(RegistrationListener.class);
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
- verify(mAdvertiser).addService(anyInt(), argThat(s ->
+ verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(s ->
"Instance".equals(s.getServiceName())
- && SERVICE_TYPE.equals(s.getServiceType())), eq("_subtype"));
+ && SERVICE_TYPE.equals(s.getServiceType())), eq("_subtype"), any());
final DiscoveryListener discListener = mock(DiscoveryListener.class);
client.discoverServices(typeWithSubtype, PROTOCOL, network, Runnable::run, discListener);
@@ -1221,8 +1222,8 @@
waitForIdle();
final ArgumentCaptor<Integer> serviceIdCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mAdvertiser).addService(serviceIdCaptor.capture(),
- argThat(info -> matches(info, regInfo)), eq(null) /* subtype */);
+ verify(mAdvertiser).addOrUpdateService(serviceIdCaptor.capture(),
+ argThat(info -> matches(info, regInfo)), eq(null) /* subtype */, any());
client.unregisterService(regListenerWithoutFeature);
waitForIdle();
@@ -1281,10 +1282,10 @@
waitForIdle();
// The advertiser is enabled for _type2 but not _type1
- verify(mAdvertiser, never()).addService(
- anyInt(), argThat(info -> matches(info, service1)), eq(null) /* subtype */);
- verify(mAdvertiser).addService(
- anyInt(), argThat(info -> matches(info, service2)), eq(null) /* subtype */);
+ verify(mAdvertiser, never()).addOrUpdateService(anyInt(),
+ argThat(info -> matches(info, service1)), eq(null) /* subtype */, any());
+ verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(info -> matches(info, service2)),
+ eq(null) /* subtype */, any());
}
@Test
@@ -1308,8 +1309,8 @@
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mAdvertiser).addService(idCaptor.capture(), argThat(info ->
- matches(info, regInfo)), eq(null) /* subtype */);
+ verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), argThat(info ->
+ matches(info, regInfo)), eq(null) /* subtype */, any());
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
@@ -1357,7 +1358,7 @@
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
- verify(mAdvertiser, never()).addService(anyInt(), any(), any());
+ verify(mAdvertiser, never()).addOrUpdateService(anyInt(), any(), any(), any());
verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
@@ -1386,9 +1387,9 @@
waitForIdle();
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
// Service name is truncated to 63 characters
- verify(mAdvertiser).addService(idCaptor.capture(),
+ verify(mAdvertiser).addOrUpdateService(idCaptor.capture(),
argThat(info -> info.getServiceName().equals("a".repeat(63))),
- eq(null) /* subtype */);
+ eq(null) /* subtype */, any());
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
@@ -1478,7 +1479,7 @@
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
- verify(mAdvertiser).addService(anyInt(), any(), any());
+ verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), any());
// Verify the discovery uses MdnsDiscoveryManager
final DiscoveryListener discListener = mock(DiscoveryListener.class);
@@ -1511,7 +1512,7 @@
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
- verify(mAdvertiser).addService(anyInt(), any(), any());
+ verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), any());
final Network wifiNetwork1 = new Network(123);
final Network wifiNetwork2 = new Network(124);
@@ -1697,8 +1698,8 @@
@Test
@EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
- public void testRegisterOffloadEngine_checkPermission()
- throws PackageManager.NameNotFoundException {
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void testRegisterOffloadEngine_checkPermission_V() {
final NsdManager client = connectClient(mService);
final OffloadEngine offloadEngine = mock(OffloadEngine.class);
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK);
@@ -1708,17 +1709,41 @@
doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
REGISTER_NSD_OFFLOAD_ENGINE);
- PermissionInfo permissionInfo = new PermissionInfo("");
- permissionInfo.packageName = "android";
- permissionInfo.protectionLevel = PROTECTION_SIGNATURE;
- doReturn(permissionInfo).when(mPackageManager).getPermissionInfo(
- REGISTER_NSD_OFFLOAD_ENGINE, 0);
- client.registerOffloadEngine("iface1", OffloadEngine.OFFLOAD_TYPE_REPLY,
- OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK,
- Runnable::run, offloadEngine);
- client.unregisterOffloadEngine(offloadEngine);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
+ REGISTER_NSD_OFFLOAD_ENGINE);
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER);
+ assertThrows(SecurityException.class,
+ () -> client.registerOffloadEngine("iface1", OffloadEngine.OFFLOAD_TYPE_REPLY,
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
+ offloadEngine));
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
+ REGISTER_NSD_OFFLOAD_ENGINE);
+ final OffloadEngine offloadEngine2 = mock(OffloadEngine.class);
+ client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY,
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
+ offloadEngine2);
+ client.unregisterOffloadEngine(offloadEngine2);
+ }
- // TODO: add checks to test the packageName other than android
+ @Test
+ @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
+ @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testRegisterOffloadEngine_checkPermission_U() {
+ final NsdManager client = connectClient(mService);
+ final OffloadEngine offloadEngine = mock(OffloadEngine.class);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
+ PERMISSION_MAINLINE_NETWORK_STACK);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS);
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
+ REGISTER_NSD_OFFLOAD_ENGINE);
+
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER);
+ client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY,
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
+ offloadEngine);
+ client.unregisterOffloadEngine(offloadEngine);
}
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index afb9abd..44512bb 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -19,6 +19,7 @@
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER;
import static android.net.NetworkCapabilities.MAX_TRANSPORT;
@@ -329,14 +330,14 @@
public void testOverrideDefaultMode() throws Exception {
// Hard-coded default is opportunistic mode.
final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx);
- assertTrue(cfgAuto.useTls);
+ assertEquals(PRIVATE_DNS_MODE_OPPORTUNISTIC, cfgAuto.mode);
assertEquals("", cfgAuto.hostname);
assertEquals(new InetAddress[0], cfgAuto.ips);
// Pretend a gservices push sets the default to "off".
ConnectivitySettingsManager.setPrivateDnsDefaultMode(mCtx, PRIVATE_DNS_MODE_OFF);
final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx);
- assertFalse(cfgOff.useTls);
+ assertEquals(PRIVATE_DNS_MODE_OFF, cfgOff.mode);
assertEquals("", cfgOff.hostname);
assertEquals(new InetAddress[0], cfgOff.ips);
@@ -344,7 +345,7 @@
ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com");
final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx);
- assertTrue(cfgStrict.useTls);
+ assertEquals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, cfgStrict.mode);
assertEquals("strictmode.com", cfgStrict.hostname);
assertEquals(new InetAddress[0], cfgStrict.ips);
}
@@ -419,7 +420,7 @@
// The PrivateDnsConfig map is empty, so the default PRIVATE_DNS_OFF is returned.
PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
- assertFalse(privateDnsCfg.useTls);
+ assertEquals(PRIVATE_DNS_MODE_OFF, privateDnsCfg.mode);
assertEquals("", privateDnsCfg.hostname);
assertEquals(new InetAddress[0], privateDnsCfg.ips);
@@ -431,7 +432,7 @@
VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
- assertTrue(privateDnsCfg.useTls);
+ assertEquals(PRIVATE_DNS_MODE_OPPORTUNISTIC, privateDnsCfg.mode);
assertEquals("", privateDnsCfg.hostname);
assertEquals(new InetAddress[0], privateDnsCfg.ips);
@@ -439,14 +440,14 @@
mDnsManager.updatePrivateDns(network, new PrivateDnsConfig(tlsName, tlsAddrs));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
- assertTrue(privateDnsCfg.useTls);
+ assertEquals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, privateDnsCfg.mode);
assertEquals(tlsName, privateDnsCfg.hostname);
assertEquals(tlsAddrs, privateDnsCfg.ips);
// The network is removed, so the PrivateDnsConfig map becomes empty again.
mDnsManager.removeNetwork(network);
privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
- assertFalse(privateDnsCfg.useTls);
+ assertEquals(PRIVATE_DNS_MODE_OFF, privateDnsCfg.mode);
assertEquals("", privateDnsCfg.hostname);
assertEquals(new InetAddress[0], privateDnsCfg.ips);
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index ea2228e..46e9e45 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -118,7 +118,6 @@
import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.LocalSocket;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
@@ -195,10 +194,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.io.BufferedWriter;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.net.Inet4Address;
@@ -209,13 +205,11 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -3178,20 +3172,7 @@
// Make it public and un-final so as to spy it
public class TestDeps extends Vpn.Dependencies {
- public final CompletableFuture<String[]> racoonArgs = new CompletableFuture();
- public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture();
- public final File mStateFile;
-
- private final HashMap<String, Boolean> mRunningServices = new HashMap<>();
-
- TestDeps() {
- try {
- mStateFile = File.createTempFile("vpnTest", ".tmp");
- mStateFile.deleteOnExit();
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
- }
+ TestDeps() {}
@Override
public boolean isCallerSystem() {
@@ -3199,89 +3180,11 @@
}
@Override
- public void startService(final String serviceName) {
- mRunningServices.put(serviceName, true);
- }
-
- @Override
- public void stopService(final String serviceName) {
- mRunningServices.put(serviceName, false);
- }
-
- @Override
- public boolean isServiceRunning(final String serviceName) {
- return mRunningServices.getOrDefault(serviceName, false);
- }
-
- @Override
- public boolean isServiceStopped(final String serviceName) {
- return !isServiceRunning(serviceName);
- }
-
- @Override
- public File getStateFile() {
- return mStateFile;
- }
-
- @Override
public PendingIntent getIntentForStatusPanel(Context context) {
return null;
}
@Override
- public void sendArgumentsToDaemon(
- final String daemon, final LocalSocket socket, final String[] arguments,
- final Vpn.RetryScheduler interruptChecker) throws IOException {
- if ("racoon".equals(daemon)) {
- racoonArgs.complete(arguments);
- } else if ("mtpd".equals(daemon)) {
- writeStateFile(arguments);
- mtpdArgs.complete(arguments);
- } else {
- throw new UnsupportedOperationException("Unsupported daemon : " + daemon);
- }
- }
-
- private void writeStateFile(final String[] arguments) throws IOException {
- mStateFile.delete();
- mStateFile.createNewFile();
- mStateFile.deleteOnExit();
- final BufferedWriter writer = new BufferedWriter(
- new FileWriter(mStateFile, false /* append */));
- writer.write(EGRESS_IFACE);
- writer.write("\n");
- // addresses
- writer.write("10.0.0.1/24\n");
- // routes
- writer.write("192.168.6.0/24\n");
- // dns servers
- writer.write("192.168.6.1\n");
- // search domains
- writer.write("vpn.searchdomains.com\n");
- // endpoint - intentionally empty
- writer.write("\n");
- writer.flush();
- writer.close();
- }
-
- @Override
- @NonNull
- public InetAddress resolve(final String endpoint) {
- try {
- // If a numeric IP address, return it.
- return InetAddress.parseNumericAddress(endpoint);
- } catch (IllegalArgumentException e) {
- // Otherwise, return some token IP to test for.
- return InetAddress.parseNumericAddress("5.6.7.8");
- }
- }
-
- @Override
- public boolean isInterfacePresent(final Vpn vpn, final String iface) {
- return true;
- }
-
- @Override
public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
return new ParcelFileDescriptor(new FileDescriptor());
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index a86f923..f0cb6df 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -19,6 +19,7 @@
import android.net.InetAddresses.parseNumericAddress
import android.net.LinkAddress
import android.net.Network
+import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.net.nsd.OffloadEngine
import android.net.nsd.OffloadServiceInfo
@@ -71,6 +72,7 @@
private val TEST_INTERFACE2 = "test_iface2"
private val TEST_OFFLOAD_PACKET1 = byteArrayOf(0x01, 0x02, 0x03)
private val TEST_OFFLOAD_PACKET2 = byteArrayOf(0x02, 0x03, 0x04)
+private val DEFAULT_ADVERTISING_OPTION = MdnsAdvertisingOptions.getDefaultOptions()
private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
port = 12345
@@ -186,7 +188,8 @@
fun testAddService_OneNetwork() {
val advertiser =
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), socketCbCaptor.capture())
@@ -247,7 +250,8 @@
fun testAddService_AllNetworks() {
val advertiser =
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
- postSync { advertiser.addService(SERVICE_ID_1, ALL_NETWORKS_SERVICE, TEST_SUBTYPE) }
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE,
+ TEST_SUBTYPE, DEFAULT_ADVERTISING_OPTION) }
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(ALL_NETWORKS_SERVICE.network),
@@ -318,24 +322,27 @@
fun testAddService_Conflicts() {
val advertiser =
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
val oneNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), oneNetSocketCbCaptor.capture())
val oneNetSocketCb = oneNetSocketCbCaptor.value
// Register a service with the same name on all networks (name conflict)
- postSync { advertiser.addService(SERVICE_ID_2, ALL_NETWORKS_SERVICE, null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_2, ALL_NETWORKS_SERVICE,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
val allNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(null), allNetSocketCbCaptor.capture())
val allNetSocketCb = allNetSocketCbCaptor.value
- postSync { advertiser.addService(LONG_SERVICE_ID_1, LONG_SERVICE_1, null /* subtype */) }
- postSync { advertiser.addService(LONG_SERVICE_ID_2, LONG_ALL_NETWORKS_SERVICE,
- null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(LONG_SERVICE_ID_1, LONG_SERVICE_1,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
+ postSync { advertiser.addOrUpdateService(LONG_SERVICE_ID_2, LONG_ALL_NETWORKS_SERVICE,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
- postSync { advertiser.addService(CASE_INSENSITIVE_TEST_SERVICE_ID, ALL_NETWORKS_SERVICE_2,
- null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(CASE_INSENSITIVE_TEST_SERVICE_ID,
+ ALL_NETWORKS_SERVICE_2, null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
// Callbacks for matching network and all networks both get the socket
postSync {
@@ -400,11 +407,51 @@
}
@Test
+ fun testAddOrUpdateService_Updates() {
+ val advertiser =
+ MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
+
+ val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
+ verify(socketProvider).requestSocket(eq(null), socketCbCaptor.capture())
+
+ val socketCb = socketCbCaptor.value
+ postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_1, mockSocket1, listOf(TEST_LINKADDR)) }
+
+ verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
+ argThat { it.matches(ALL_NETWORKS_SERVICE) }, eq(null))
+
+ val updateOptions = MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(true).build()
+
+ // Update with serviceId that is not registered yet should fail
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_2, ALL_NETWORKS_SERVICE, TEST_SUBTYPE,
+ updateOptions) }
+ verify(cb).onRegisterServiceFailed(SERVICE_ID_2, NsdManager.FAILURE_INTERNAL_ERROR)
+
+ // Update service with different NsdServiceInfo should fail
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1, TEST_SUBTYPE,
+ updateOptions) }
+ verify(cb).onRegisterServiceFailed(SERVICE_ID_1, NsdManager.FAILURE_INTERNAL_ERROR)
+
+ // Update service with same NsdServiceInfo but different subType should succeed
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE, TEST_SUBTYPE,
+ updateOptions) }
+ verify(mockInterfaceAdvertiser1).updateService(eq(SERVICE_ID_1), eq(TEST_SUBTYPE))
+
+ // Newly created MdnsInterfaceAdvertiser will get addService() call.
+ postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_2, mockSocket2, listOf(TEST_LINKADDR2)) }
+ verify(mockInterfaceAdvertiser2).addService(eq(SERVICE_ID_1),
+ argThat { it.matches(ALL_NETWORKS_SERVICE) }, eq(TEST_SUBTYPE))
+ }
+
+ @Test
fun testRemoveService_whenAllServiceRemoved_thenUpdateHostName() {
val advertiser =
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
verify(mockDeps, times(1)).generateHostname()
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
+ postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
+ null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
postSync { advertiser.removeService(SERVICE_ID_1) }
verify(mockDeps, times(2)).generateHostname()
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
index db41a6a..f85d71d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -48,6 +48,7 @@
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -59,6 +60,7 @@
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
private const val TEST_SERVICE_ID_1 = 42
+private const val TEST_SERVICE_ID_DUPLICATE = 43
private val TEST_SERVICE_1 = NsdServiceInfo().apply {
serviceType = "_testservice._tcp"
serviceName = "MyTestService"
@@ -272,6 +274,28 @@
verify(prober).restartForConflict(mockProbingInfo)
}
+ @Test
+ fun testReplaceExitingService() {
+ doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
+ .addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
+ val subType = "_sub"
+ advertiser.addService(TEST_SERVICE_ID_DUPLICATE, TEST_SERVICE_1, subType)
+ verify(repository).addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
+ verify(announcer).stop(TEST_SERVICE_ID_DUPLICATE)
+ verify(prober).startProbing(any())
+ }
+
+ @Test
+ fun testUpdateExistingService() {
+ doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
+ .addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
+ val subType = "_sub"
+ advertiser.updateService(TEST_SERVICE_ID_DUPLICATE, subType)
+ verify(repository).updateService(eq(TEST_SERVICE_ID_DUPLICATE), any())
+ verify(announcer, never()).stop(TEST_SERVICE_ID_DUPLICATE)
+ verify(prober, never()).startProbing(any())
+ }
+
private fun addServiceAndFinishProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
AnnouncementInfo {
val testProbingInfo = mock(ProbingInfo::class.java)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
index f26f7e1..582e7b1 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -129,7 +129,7 @@
@Test
fun testAddAndConflicts() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
assertFailsWith(NameConflictException::class) {
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */)
}
@@ -139,6 +139,45 @@
}
@Test
+ fun testAddAndUpdates() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+
+ assertFailsWith(IllegalArgumentException::class) {
+ repository.updateService(TEST_SERVICE_ID_2, null /* subtype */)
+ }
+
+ repository.updateService(TEST_SERVICE_ID_1, TEST_SUBTYPE)
+
+ val queriedName = arrayOf(TEST_SUBTYPE, "_sub", "_testservice", "_tcp", "local")
+ val questions = listOf(MdnsPointerRecord(queriedName,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ // TTL and data is empty for a question
+ 0L /* ttlMillis */,
+ null /* pointer */))
+ val query = MdnsPacket(0 /* flags */, questions, listOf() /* answers */,
+ listOf() /* authorityRecords */, listOf() /* additionalRecords */)
+ val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val reply = repository.getReply(query, src)
+
+ assertNotNull(reply)
+
+ // TTLs as per RFC6762 10.
+ val longTtl = 4_500_000L
+ val serviceName = arrayOf("MyTestService", "_testservice", "_tcp", "local")
+
+ assertEquals(listOf(
+ MdnsPointerRecord(
+ queriedName,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ longTtl,
+ serviceName),
+ ), reply.answers)
+ }
+
+ @Test
fun testInvalidReuseOfServiceId() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
@@ -758,7 +797,7 @@
private fun MdnsRecordRepository.initWithService(
serviceId: Int,
serviceInfo: NsdServiceInfo,
- subtype: String? = null
+ subtype: String? = null,
): AnnouncementInfo {
updateAddresses(TEST_ADDRESSES)
addService(serviceId, serviceInfo, subtype)
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index f4c62c5..958c4f2 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -132,6 +132,7 @@
it[ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION] = true
it[ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION] = true
it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
+ it[ConnectivityService.LOG_BPF_RC] = true
}
fun enableFeature(f: String) = enabledFeatures.set(f, true)
fun disableFeature(f: String) = enabledFeatures.set(f, false)