Merge "[XFRM_MSG_NEWSA] Support xfrm_stats and xfrm_usersa_info" into main
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 6f3e865..528991f 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -189,7 +189,10 @@
return cachedAddress;
}
- for (IpPrefix prefixRange : mTetheringPrefixes) {
+ final int prefixIndex = getStartedPrefixIndex();
+ for (int i = 0; i < mTetheringPrefixes.size(); i++) {
+ final IpPrefix prefixRange = mTetheringPrefixes.get(
+ (prefixIndex + i) % mTetheringPrefixes.size());
final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
if (newAddress != null) {
mDownstreams.add(ipServer);
@@ -202,6 +205,28 @@
return null;
}
+ private int getStartedPrefixIndex() {
+ if (!mConfig.isRandomPrefixBaseEnabled()) return 0;
+
+ final int random = getRandomInt() & 0xffffff;
+ // This is to select the starting prefix range (/8, /12, or /16) instead of the actual
+ // LinkAddress. To avoid complex operations in the selection logic and make the selected
+ // rate approximate consistency with that /8 is around 2^4 times of /12 and /12 is around
+ // 2^4 times of /16, we simply define a map between the value and the prefix value like
+ // this:
+ //
+ // Value 0 ~ 0xffff (65536/16777216 = 0.39%) -> 192.168.0.0/16
+ // Value 0x10000 ~ 0xfffff (983040/16777216 = 5.86%) -> 172.16.0.0/12
+ // Value 0x100000 ~ 0xffffff (15728640/16777216 = 93.7%) -> 10.0.0.0/8
+ if (random > 0xfffff) {
+ return 2;
+ } else if (random > 0xffff) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
private int getPrefixBaseAddress(final IpPrefix prefix) {
return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
}
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index da3b584..5022b40 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -303,9 +303,9 @@
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mRoutingCoordinator = mDeps.getRoutingCoordinator(mContext);
- mLooper = mDeps.getTetheringLooper();
- mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
- mTetheringMetrics = mDeps.getTetheringMetrics();
+ mLooper = mDeps.makeTetheringLooper();
+ mNotificationUpdater = mDeps.makeNotificationUpdater(mContext, mLooper);
+ mTetheringMetrics = mDeps.makeTetheringMetrics();
// This is intended to ensrure that if something calls startTethering(bluetooth) just after
// bluetooth is enabled. Before onServiceConnected is called, store the calls into this
@@ -319,7 +319,7 @@
mTetherMainSM.start();
mHandler = mTetherMainSM.getHandler();
- mOffloadController = mDeps.getOffloadController(mHandler, mLog,
+ mOffloadController = mDeps.makeOffloadController(mHandler, mLog,
new OffloadController.Dependencies() {
@Override
@@ -327,7 +327,7 @@
return mConfig;
}
});
- mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mHandler, mLog,
+ mUpstreamNetworkMonitor = mDeps.makeUpstreamNetworkMonitor(mContext, mHandler, mLog,
(what, obj) -> {
mTetherMainSM.sendMessage(TetherMainSM.EVENT_UPSTREAM_CALLBACK, what, 0, obj);
});
@@ -337,7 +337,7 @@
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
// EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
// permission is changed according to entitlement check result.
- mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog,
+ mEntitlementMgr = mDeps.makeEntitlementManager(mContext, mHandler, mLog,
() -> mTetherMainSM.sendMessage(
TetherMainSM.EVENT_UPSTREAM_PERMISSION_CHANGED));
mEntitlementMgr.setOnTetherProvisioningFailedListener((downstream, reason) -> {
@@ -373,11 +373,11 @@
// It is OK for the configuration to be passed to the PrivateAddressCoordinator at
// construction time because the only part of the configuration it uses is
// shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
- mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig);
+ mPrivateAddressCoordinator = mDeps.makePrivateAddressCoordinator(mContext, mConfig);
// Must be initialized after tethering configuration is loaded because BpfCoordinator
// constructor needs to use the configuration.
- mBpfCoordinator = mDeps.getBpfCoordinator(
+ mBpfCoordinator = mDeps.makeBpfCoordinator(
new BpfCoordinator.Dependencies() {
@NonNull
public Handler getHandler() {
@@ -406,7 +406,7 @@
});
if (SdkLevel.isAtLeastT() && mConfig.isWearTetheringEnabled()) {
- mWearableConnectionManager = mDeps.getWearableConnectionManager(mContext);
+ mWearableConnectionManager = mDeps.makeWearableConnectionManager(mContext);
} else {
mWearableConnectionManager = null;
}
@@ -875,7 +875,7 @@
private void changeBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
final boolean enable) {
- final BluetoothPanShim panShim = mDeps.getBluetoothPanShim(bluetoothPan);
+ final BluetoothPanShim panShim = mDeps.makeBluetoothPanShim(bluetoothPan);
if (enable) {
if (mBluetoothIfaceRequest != null) {
Log.d(TAG, "Bluetooth tethering settings already enabled");
@@ -1739,7 +1739,7 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
+ mIPv6TetheringCoordinator = deps.makeIPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
setInitialState(mInitialState);
@@ -2844,7 +2844,7 @@
new IpServer(iface, mHandler, interfaceType, mLog, mNetd, mBpfCoordinator,
mRoutingCoordinator, new ControlCallback(), mConfig,
mPrivateAddressCoordinator, mTetheringMetrics,
- mDeps.getIpServerDependencies()), isNcm);
+ mDeps.makeIpServerDependencies()), isNcm);
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 502fee8..0678525 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -130,6 +130,8 @@
public static final String TETHER_ENABLE_WEAR_TETHERING =
"tether_enable_wear_tethering";
+ public static final String TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION =
+ "tether_force_random_prefix_base_selection";
/**
* Default value that used to periodic polls tether offload stats from tethering offload HAL
* to make the data warnings work.
@@ -171,6 +173,7 @@
private final int mP2pLeasesSubnetPrefixLength;
private final boolean mEnableWearTethering;
+ private final boolean mRandomPrefixBase;
private final int mUsbTetheringFunction;
protected final ContentResolver mContentResolver;
@@ -288,6 +291,8 @@
mEnableWearTethering = shouldEnableWearTethering(ctx);
+ mRandomPrefixBase = mDeps.isFeatureEnabled(ctx, TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION);
+
configLog.log(toString());
}
@@ -376,6 +381,10 @@
return mEnableWearTethering;
}
+ public boolean isRandomPrefixBaseEnabled() {
+ return mRandomPrefixBase;
+ }
+
/** Does the dumping.*/
public void dump(PrintWriter pw) {
pw.print("activeDataSubId: ");
@@ -426,6 +435,9 @@
pw.print("mUsbTetheringFunction: ");
pw.println(isUsingNcm() ? "NCM" : "RNDIS");
+
+ pw.print("mRandomPrefixBase: ");
+ pw.println(mRandomPrefixBase);
}
/** Returns the string representation of this object.*/
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index d02e8e8..9dfd225 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -53,58 +53,58 @@
*/
public abstract class TetheringDependencies {
/**
- * Get a reference to the BpfCoordinator to be used by tethering.
+ * Make the BpfCoordinator to be used by tethering.
*/
- public @NonNull BpfCoordinator getBpfCoordinator(
+ public @NonNull BpfCoordinator makeBpfCoordinator(
@NonNull BpfCoordinator.Dependencies deps) {
return new BpfCoordinator(deps);
}
/**
- * Get a reference to the offload hardware interface to be used by tethering.
+ * Make the offload hardware interface to be used by tethering.
*/
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+ public OffloadHardwareInterface makeOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
/**
- * Get a reference to the offload controller to be used by tethering.
+ * Make the offload controller to be used by tethering.
*/
@NonNull
- public OffloadController getOffloadController(@NonNull Handler h,
+ public OffloadController makeOffloadController(@NonNull Handler h,
@NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
final NetworkStatsManager statsManager =
(NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
- return new OffloadController(h, getOffloadHardwareInterface(h, log),
+ return new OffloadController(h, makeOffloadHardwareInterface(h, log),
getContext().getContentResolver(), statsManager, log, deps);
}
/**
- * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+ * Make the UpstreamNetworkMonitor to be used by tethering.
*/
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, Handler h,
+ public UpstreamNetworkMonitor makeUpstreamNetworkMonitor(Context ctx, Handler h,
SharedLog log, UpstreamNetworkMonitor.EventListener listener) {
return new UpstreamNetworkMonitor(ctx, h, log, listener);
}
/**
- * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+ * Make the IPv6TetheringCoordinator to be used by tethering.
*/
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ public IPv6TetheringCoordinator makeIPv6TetheringCoordinator(
ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
/**
- * Get dependencies to be used by IpServer.
+ * Make dependencies to be used by IpServer.
*/
- public abstract IpServer.Dependencies getIpServerDependencies();
+ public abstract IpServer.Dependencies makeIpServerDependencies();
/**
- * Get a reference to the EntitlementManager to be used by tethering.
+ * Make the EntitlementManager to be used by tethering.
*/
- public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+ public EntitlementManager makeEntitlementManager(Context ctx, Handler h, SharedLog log,
Runnable callback) {
return new EntitlementManager(ctx, h, log, callback);
}
@@ -136,17 +136,17 @@
}
/**
- * Get a reference to the TetheringNotificationUpdater to be used by tethering.
+ * Make the TetheringNotificationUpdater to be used by tethering.
*/
- public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
+ public TetheringNotificationUpdater makeNotificationUpdater(@NonNull final Context ctx,
@NonNull final Looper looper) {
return new TetheringNotificationUpdater(ctx, looper);
}
/**
- * Get tethering thread looper.
+ * Make tethering thread looper.
*/
- public abstract Looper getTetheringLooper();
+ public abstract Looper makeTetheringLooper();
/**
* Get Context of TetheringService.
@@ -166,26 +166,26 @@
}
/**
- * Get a reference to PrivateAddressCoordinator to be used by Tethering.
+ * Make PrivateAddressCoordinator to be used by Tethering.
*/
- public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+ public PrivateAddressCoordinator makePrivateAddressCoordinator(Context ctx,
TetheringConfiguration cfg) {
return new PrivateAddressCoordinator(ctx, cfg);
}
/**
- * Get BluetoothPanShim object to enable/disable bluetooth tethering.
+ * Make BluetoothPanShim object to enable/disable bluetooth tethering.
*
* TODO: use BluetoothPan directly when mainline module is built with API 32.
*/
- public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
+ public BluetoothPanShim makeBluetoothPanShim(BluetoothPan pan) {
return BluetoothPanShimImpl.newInstance(pan);
}
/**
- * Get a reference to the TetheringMetrics to be used by tethering.
+ * Make the TetheringMetrics to be used by tethering.
*/
- public TetheringMetrics getTetheringMetrics() {
+ public TetheringMetrics makeTetheringMetrics() {
return new TetheringMetrics();
}
@@ -193,7 +193,7 @@
* Returns the implementation of WearableConnectionManager.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public WearableConnectionManager getWearableConnectionManager(Context ctx) {
+ public WearableConnectionManager makeWearableConnectionManager(Context ctx) {
return new WearableConnectionManager(ctx);
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 96ddfa0..aa73819 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -322,7 +322,7 @@
public TetheringDependencies makeTetheringDependencies() {
return new TetheringDependencies() {
@Override
- public Looper getTetheringLooper() {
+ public Looper makeTetheringLooper() {
final HandlerThread tetherThread = new HandlerThread("android.tethering");
tetherThread.start();
return tetherThread.getLooper();
@@ -334,7 +334,7 @@
}
@Override
- public IpServer.Dependencies getIpServerDependencies() {
+ public IpServer.Dependencies makeIpServerDependencies() {
return new IpServer.Dependencies() {
@Override
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 6ebd6ae..2298a1a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -30,6 +30,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -104,6 +105,7 @@
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
+ when(mConfig.isRandomPrefixBaseEnabled()).thenReturn(false);
setUpIpServers();
mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
}
@@ -572,4 +574,41 @@
assertEquals("Wrong local hotspot prefix: ", new LinkAddress("192.168.134.5/24"),
localHotspotAddress);
}
+
+ @Test
+ public void testStartedPrefixRange() throws Exception {
+ when(mConfig.isRandomPrefixBaseEnabled()).thenReturn(true);
+
+ startedPrefixBaseTest("192.168.0.0/16", 0);
+
+ startedPrefixBaseTest("192.168.0.0/16", 1);
+
+ startedPrefixBaseTest("192.168.0.0/16", 0xffff);
+
+ startedPrefixBaseTest("172.16.0.0/12", 0x10000);
+
+ startedPrefixBaseTest("172.16.0.0/12", 0x11111);
+
+ startedPrefixBaseTest("172.16.0.0/12", 0xfffff);
+
+ startedPrefixBaseTest("10.0.0.0/8", 0x100000);
+
+ startedPrefixBaseTest("10.0.0.0/8", 0x1fffff);
+
+ startedPrefixBaseTest("10.0.0.0/8", 0xffffff);
+
+ startedPrefixBaseTest("192.168.0.0/16", 0x1000000);
+ }
+
+ private void startedPrefixBaseTest(final String expected, final int randomIntForPrefixBase)
+ throws Exception {
+ mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomIntForPrefixBase);
+ final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
+ final IpPrefix prefixBase = new IpPrefix(expected);
+ assertTrue(address + " is not part of " + prefixBase,
+ prefixBase.containsPrefix(asIpPrefix(address)));
+
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 5877fc5..82b8845 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -436,51 +436,51 @@
ArrayList<IpServer> mAllDownstreams;
@Override
- public BpfCoordinator getBpfCoordinator(
+ public BpfCoordinator makeBpfCoordinator(
BpfCoordinator.Dependencies deps) {
return mBpfCoordinator;
}
@Override
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+ public OffloadHardwareInterface makeOffloadHardwareInterface(Handler h, SharedLog log) {
return mOffloadHardwareInterface;
}
@Override
- public OffloadController getOffloadController(Handler h, SharedLog log,
+ public OffloadController makeOffloadController(Handler h, SharedLog log,
OffloadController.Dependencies deps) {
- mOffloadCtrl = spy(super.getOffloadController(h, log, deps));
+ mOffloadCtrl = spy(super.makeOffloadController(h, log, deps));
// Return real object here instead of mock because
// testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object.
return mOffloadCtrl;
}
@Override
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, Handler h,
+ public UpstreamNetworkMonitor makeUpstreamNetworkMonitor(Context ctx, Handler h,
SharedLog log, UpstreamNetworkMonitor.EventListener listener) {
// Use a real object instead of a mock so that some tests can use a real UNM and some
// can use a mock.
mEventListener = listener;
- mUpstreamNetworkMonitor = spy(super.getUpstreamNetworkMonitor(ctx, h, log, listener));
+ mUpstreamNetworkMonitor = spy(super.makeUpstreamNetworkMonitor(ctx, h, log, listener));
return mUpstreamNetworkMonitor;
}
@Override
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ public IPv6TetheringCoordinator makeIPv6TetheringCoordinator(
ArrayList<IpServer> notifyList, SharedLog log) {
mAllDownstreams = notifyList;
return mIPv6TetheringCoordinator;
}
@Override
- public IpServer.Dependencies getIpServerDependencies() {
+ public IpServer.Dependencies makeIpServerDependencies() {
return mIpServerDependencies;
}
@Override
- public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+ public EntitlementManager makeEntitlementManager(Context ctx, Handler h, SharedLog log,
Runnable callback) {
- mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback));
+ mEntitleMgr = spy(super.makeEntitlementManager(ctx, h, log, callback));
return mEntitleMgr;
}
@@ -503,7 +503,7 @@
}
@Override
- public Looper getTetheringLooper() {
+ public Looper makeTetheringLooper() {
return mLooper.getLooper();
}
@@ -518,7 +518,7 @@
}
@Override
- public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
+ public TetheringNotificationUpdater makeNotificationUpdater(Context ctx, Looper looper) {
return mNotificationUpdater;
}
@@ -528,19 +528,19 @@
}
@Override
- public TetheringMetrics getTetheringMetrics() {
+ public TetheringMetrics makeTetheringMetrics() {
return mTetheringMetrics;
}
@Override
- public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+ public PrivateAddressCoordinator makePrivateAddressCoordinator(Context ctx,
TetheringConfiguration cfg) {
- mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
+ mPrivateAddressCoordinator = super.makePrivateAddressCoordinator(ctx, cfg);
return mPrivateAddressCoordinator;
}
@Override
- public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
+ public BluetoothPanShim makeBluetoothPanShim(BluetoothPan pan) {
try {
when(mBluetoothPanShim.requestTetheredInterface(
any(), any())).thenReturn(mTetheredInterfaceRequestShim);
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index ca297e5..5403be7 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -26,6 +26,7 @@
#include <bpf/BpfClassic.h>
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <nativehelper/JNIPlatformHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <utils/Log.h>
#include "jni.h"
@@ -240,6 +241,15 @@
trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale);
}
+static void android_net_utils_setsockoptBytes(JNIEnv *env, jclass clazz, jobject javaFd,
+ jint level, jint option, jbyteArray javaBytes) {
+ int sock = AFileDescriptor_getFd(env, javaFd);
+ ScopedByteArrayRO value(env, javaBytes);
+ if (setsockopt(sock, level, option, value.get(), value.size()) != 0) {
+ jniThrowErrnoException(env, "setsockoptBytes", errno);
+ }
+}
+
// ----------------------------------------------------------------------------
/*
@@ -260,6 +270,8 @@
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
+ { "setsockoptBytes", "(Ljava/io/FileDescriptor;II[B)V",
+ (void*) android_net_utils_setsockoptBytes},
};
// clang-format on
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/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
index 2679b62..fbdc024 100644
--- a/framework/src/android/net/NetworkUtils.java
+++ b/framework/src/android/net/NetworkUtils.java
@@ -426,4 +426,16 @@
return routedIPCount;
}
+ /**
+ * Sets a socket option with byte array
+ *
+ * @param fd The socket file descriptor
+ * @param level The level at which the option is defined
+ * @param option The socket option for which the value is to be set
+ * @param value The option value to be set in byte array
+ * @throws ErrnoException if setsockopt fails
+ */
+ public static native void setsockoptBytes(FileDescriptor fd, int level, int option,
+ byte[] value) throws ErrnoException;
+
}
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 2a9892f..a00c363 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -53,14 +53,10 @@
bpf_attach_type type) {
unique_fd cgroupProg(retrieveProgram(programPath));
if (!cgroupProg.ok()) {
- const int err = errno;
- ALOGE("Failed to get program from %s: %s", programPath, strerror(err));
- return statusFromErrno(err, "cgroup program get failed");
+ return statusFromErrno(errno, fmt::format("Failed to get program from {}", programPath));
}
if (android::bpf::attachProgram(type, cgroupProg, cgroupFd)) {
- const int err = errno;
- ALOGE("Program from %s attach failed: %s", programPath, strerror(err));
- return statusFromErrno(err, "program attach failed");
+ return statusFromErrno(errno, fmt::format("Program {} attach failed", programPath));
}
return netdutils::status::ok;
}
@@ -68,50 +64,38 @@
static Status checkProgramAccessible(const char* programPath) {
unique_fd prog(retrieveProgram(programPath));
if (!prog.ok()) {
- const int err = errno;
- ALOGE("Failed to get program from %s: %s", programPath, strerror(err));
- return statusFromErrno(err, "program retrieve failed");
+ return statusFromErrno(errno, fmt::format("Failed to get program from {}", programPath));
}
return netdutils::status::ok;
}
static Status initPrograms(const char* cg2_path) {
- if (!cg2_path) {
- ALOGE("cg2_path is NULL");
- return statusFromErrno(EINVAL, "cg2_path is NULL");
- }
+ if (!cg2_path) return Status("cg2_path is NULL");
// This code was mainlined in T, so this should be trivially satisfied.
- if (!modules::sdklevel::IsAtLeastT()) {
- ALOGE("S- platform is unsupported");
- abort();
- }
+ if (!modules::sdklevel::IsAtLeastT()) return Status("S- platform is unsupported");
// S requires eBPF support which was only added in 4.9, so this should be satisfied.
if (!bpf::isAtLeastKernelVersion(4, 9, 0)) {
- ALOGE("kernel version < 4.9.0 is unsupported");
- abort();
+ return Status("kernel version < 4.9.0 is unsupported");
}
// U bumps the kernel requirement up to 4.14
if (modules::sdklevel::IsAtLeastU() && !bpf::isAtLeastKernelVersion(4, 14, 0)) {
- ALOGE("U+ platform with kernel version < 4.14.0 is unsupported");
- abort();
+ return Status("U+ platform with kernel version < 4.14.0 is unsupported");
}
if (modules::sdklevel::IsAtLeastV()) {
// V bumps the kernel requirement up to 4.19
// see also: //system/netd/tests/kernel_test.cpp TestKernel419
if (!bpf::isAtLeastKernelVersion(4, 19, 0)) {
- ALOGE("V+ platform with kernel version < 4.19.0 is unsupported");
- abort();
+ return Status("V+ platform with kernel version < 4.19.0 is unsupported");
}
// Technically already required by U, but only enforce on V+
// see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
if (bpf::isKernel32Bit() && bpf::isAtLeastKernelVersion(5, 16, 0)) {
- ALOGE("V+ platform with 32 bit kernel, version >= 5.16.0 is unsupported");
- abort();
+ return Status("V+ platform with 32 bit kernel, version >= 5.16.0 is unsupported");
}
}
@@ -123,14 +107,12 @@
// problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
// see also: //system/bpf/bpfloader/BpfLoader.cpp main()
if (bpf::isUserspace32bit() && bpf::isAtLeastKernelVersion(6, 2, 0)) {
- ALOGE("32 bit userspace with Kernel version >= 6.2.0 is unsupported");
- abort();
+ return Status("32 bit userspace with Kernel version >= 6.2.0 is unsupported");
}
// U mandates this mount point (though it should also be the case on T)
if (modules::sdklevel::IsAtLeastU() && !!strcmp(cg2_path, "/sys/fs/cgroup")) {
- ALOGE("U+ platform with cg2_path != /sys/fs/cgroup is unsupported");
- abort();
+ return Status("U+ platform with cg2_path != /sys/fs/cgroup is unsupported");
}
unique_fd cg_fd(open(cg2_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
diff --git a/netd/NetdUpdatable.cpp b/netd/NetdUpdatable.cpp
index 41b1fdb..8b9e5a7 100644
--- a/netd/NetdUpdatable.cpp
+++ b/netd/NetdUpdatable.cpp
@@ -31,7 +31,7 @@
android::netdutils::Status ret = sBpfHandler.init(cg2_path);
if (!android::netdutils::isOk(ret)) {
- LOG(ERROR) << __func__ << ": BPF handler init failed";
+ LOG(ERROR) << __func__ << ": Failed. " << ret.code() << " " << ret.msg();
return -ret.code();
}
return 0;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 8cf6db7..2640332 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -91,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;
@@ -850,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 {
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/staticlibs/netd/libnetdutils/include/netdutils/Status.h b/staticlibs/netd/libnetdutils/include/netdutils/Status.h
index 7b0bd47..34f3bb2 100644
--- a/staticlibs/netd/libnetdutils/include/netdutils/Status.h
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Status.h
@@ -41,6 +41,9 @@
// Constructs an error Status, |code| must be non-zero.
Status(int code, std::string msg) : mCode(code), mMsg(std::move(msg)) { assert(!ok()); }
+ // Constructs an error Status with message. Error |code| is unspecified.
+ explicit Status(std::string msg) : Status(std::numeric_limits<int>::max(), std::move(msg)) {}
+
Status(android::base::Result<void> result)
: mCode(result.ok() ? 0 : static_cast<int>(result.error().code())),
mMsg(result.ok() ? "" : result.error().message()) {}
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/android/net/NetworkUtilsTest.java b/tests/unit/java/android/net/NetworkUtilsTest.java
index a28245d..5d789b4 100644
--- a/tests/unit/java/android/net/NetworkUtilsTest.java
+++ b/tests/unit/java/android/net/NetworkUtilsTest.java
@@ -16,19 +16,32 @@
package android.net;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVTIMEO;
import static junit.framework.Assert.assertEquals;
import android.os.Build;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.SocketUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.FileDescriptor;
+import java.io.IOException;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.TreeSet;
@RunWith(DevSdkIgnoreRunner.class)
@@ -131,4 +144,27 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
+
+ private byte[] getTimevalBytes(StructTimeval tv) {
+ byte[] timeval = new byte[16];
+ ByteBuffer buf = ByteBuffer.wrap(timeval);
+ buf.order(ByteOrder.nativeOrder());
+ buf.putLong(tv.tv_sec);
+ buf.putLong(tv.tv_usec);
+ return timeval;
+ }
+
+ @Test
+ public void testSetSockOptBytes() throws ErrnoException {
+ final FileDescriptor sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+ final StructTimeval writeTimeval = StructTimeval.fromMillis(1200);
+ byte[] timeval = getTimevalBytes(writeTimeval);
+ final StructTimeval readTimeval;
+
+ NetworkUtils.setsockoptBytes(sock, SOL_SOCKET, SO_RCVTIMEO, timeval);
+ readTimeval = Os.getsockoptTimeval(sock, SOL_SOCKET, SO_RCVTIMEO);
+
+ assertEquals(writeTimeval, readTimeval);
+ SocketUtils.closeSocketQuietly(sock);
+ }
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index ad87d28..32014c2 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -1115,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);
@@ -1222,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();
@@ -1282,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
@@ -1309,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();
@@ -1358,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));
@@ -1387,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();
@@ -1479,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);
@@ -1512,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);
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)