Merge "ethernet: inline IpClient provisioning"
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 38f1e9c..da81bda 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -666,7 +666,7 @@
// Calling System.gc() or System.runFinalization() doesn't guarantee GCs or finalizers
// are executed synchronously. The finalizer is called after GC on a separate thread.
- final int attempts = 100;
+ final int attempts = 600;
final long waitIntervalMs = 50;
for (int i = 0; i < attempts; i++) {
forceGc();
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index c2d245c..87b0a64 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -260,6 +260,7 @@
public class IpSecManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void startTunnelModeTransformMigration(@NonNull android.net.IpSecTransform, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
diff --git a/framework-t/src/android/net/IpSecManager.java b/framework-t/src/android/net/IpSecManager.java
index ff021d6..c8469b1 100644
--- a/framework-t/src/android/net/IpSecManager.java
+++ b/framework-t/src/android/net/IpSecManager.java
@@ -823,16 +823,18 @@
* Update the underlying network for this IpSecTunnelInterface.
*
* <p>This new underlying network will be used for all transforms applied AFTER this call is
- * complete. Before new {@link IpSecTransform}(s) with matching addresses are applied to
- * this tunnel interface, traffic will still use the old SA, and be routed on the old
+ * complete. Before {@link IpSecTransform}(s) with matching addresses are applied to this
+ * tunnel interface, traffic will still use the old transform, and be routed on the old
* underlying network.
*
* <p>To migrate IPsec tunnel mode traffic, a caller should:
*
* <ol>
* <li>Update the IpSecTunnelInterface’s underlying network.
- * <li>Apply {@link IpSecTransform}(s) with matching addresses to this
- * IpSecTunnelInterface.
+ * <li>Apply the new {@link IpSecTransform}(s) to this IpSecTunnelInterface. These can be
+ * new {@link IpSecTransform}(s) with matching addresses, or {@link IpSecTransform}(s)
+ * that have started migration (see {@link
+ * IpSecManager#startTunnelModeTransformMigration}).
* </ol>
*
* @param underlyingNetwork the new {@link Network} that will carry traffic for this tunnel.
@@ -841,7 +843,6 @@
* method will throw an {@link IllegalArgumentException}. If the IpSecTunnelInterface is
* later added to this network, all outbound traffic will be blackholed.
*/
- // TODO: b/169171001 Update the documentation when transform migration is supported.
// The purpose of making updating network and applying transforms separate is to leave open
// the possibility to support lossless migration procedures. To do that, Android platform
// will need to support multiple inbound tunnel mode transforms, just like it can support
@@ -1033,9 +1034,10 @@
* @param newDestinationAddress the new destination address
* @hide
*/
+ @SystemApi
@RequiresFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
- public void startMigration(
+ public void startTunnelModeTransformMigration(
@NonNull IpSecTransform transform,
@NonNull InetAddress newSourceAddress,
@NonNull InetAddress newDestinationAddress) {
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 3a17bdd..6206d83 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -2817,7 +2817,7 @@
return;
}
if (map.isEmpty()) {
- pw.println("No entries");
+ pw.println("");
return;
}
// If there is a concurrent entry deletion, value could be null. http://b/220084230.
diff --git a/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index f7871f3..fdd1478 100644
--- a/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Network;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Pair;
import com.android.server.connectivity.mdns.util.MdnsLogger;
@@ -58,26 +60,29 @@
}
}
- private final WeakReference<MdnsSocketClient> weakRequestSender;
+ private final WeakReference<MdnsSocketClientBase> weakRequestSender;
private final MdnsPacketWriter packetWriter;
private final String[] serviceTypeLabels;
private final List<String> subtypes;
private final boolean expectUnicastResponse;
private final int transactionId;
+ private final Network network;
EnqueueMdnsQueryCallable(
- @NonNull MdnsSocketClient requestSender,
+ @NonNull MdnsSocketClientBase requestSender,
@NonNull MdnsPacketWriter packetWriter,
@NonNull String serviceType,
@NonNull Collection<String> subtypes,
boolean expectUnicastResponse,
- int transactionId) {
+ int transactionId,
+ @Nullable Network network) {
weakRequestSender = new WeakReference<>(requestSender);
this.packetWriter = packetWriter;
serviceTypeLabels = TextUtils.split(serviceType, "\\.");
this.subtypes = new ArrayList<>(subtypes);
this.expectUnicastResponse = expectUnicastResponse;
this.transactionId = transactionId;
+ this.network = network;
}
// Incompatible return type for override of Callable#call().
@@ -86,7 +91,7 @@
@Nullable
public Pair<Integer, List<String>> call() {
try {
- MdnsSocketClient requestSender = weakRequestSender.get();
+ MdnsSocketClientBase requestSender = weakRequestSender.get();
if (requestSender == null) {
return null;
}
@@ -127,15 +132,24 @@
MdnsConstants.QCLASS_INTERNET
| (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0));
- InetAddress mdnsAddress = MdnsConstants.getMdnsIPv4Address();
- if (requestSender.isOnIPv6OnlyNetwork()) {
- mdnsAddress = MdnsConstants.getMdnsIPv6Address();
- }
+ if (requestSender instanceof MdnsMultinetworkSocketClient) {
+ sendPacketToIpv4AndIpv6(requestSender, MdnsConstants.MDNS_PORT, network);
+ for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
+ sendPacketToIpv4AndIpv6(requestSender, emulatorPort, network);
+ }
+ } else if (requestSender instanceof MdnsSocketClient) {
+ final MdnsSocketClient client = (MdnsSocketClient) requestSender;
+ InetAddress mdnsAddress = MdnsConstants.getMdnsIPv4Address();
+ if (client.isOnIPv6OnlyNetwork()) {
+ mdnsAddress = MdnsConstants.getMdnsIPv6Address();
+ }
- sendPacketTo(requestSender,
- new InetSocketAddress(mdnsAddress, MdnsConstants.MDNS_PORT));
- for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
- sendPacketTo(requestSender, new InetSocketAddress(mdnsAddress, emulatorPort));
+ sendPacketTo(client, new InetSocketAddress(mdnsAddress, MdnsConstants.MDNS_PORT));
+ for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
+ sendPacketTo(client, new InetSocketAddress(mdnsAddress, emulatorPort));
+ }
+ } else {
+ throw new IOException("Unknown socket client type: " + requestSender.getClass());
}
return Pair.create(transactionId, subtypes);
} catch (IOException e) {
@@ -145,7 +159,7 @@
}
}
- private void sendPacketTo(MdnsSocketClient requestSender, InetSocketAddress address)
+ private void sendPacketTo(MdnsSocketClientBase requestSender, InetSocketAddress address)
throws IOException {
DatagramPacket packet = packetWriter.getPacket(address);
if (expectUnicastResponse) {
@@ -154,4 +168,31 @@
requestSender.sendMulticastPacket(packet);
}
}
+
+ private void sendPacketFromNetwork(MdnsSocketClientBase requestSender,
+ InetSocketAddress address, Network network)
+ throws IOException {
+ DatagramPacket packet = packetWriter.getPacket(address);
+ if (expectUnicastResponse) {
+ requestSender.sendUnicastPacket(packet, network);
+ } else {
+ requestSender.sendMulticastPacket(packet, network);
+ }
+ }
+
+ private void sendPacketToIpv4AndIpv6(MdnsSocketClientBase requestSender, int port,
+ Network network) {
+ try {
+ sendPacketFromNetwork(requestSender,
+ new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), port), network);
+ } catch (IOException e) {
+ Log.i(TAG, "Can't send packet to IPv4", e);
+ }
+ try {
+ sendPacketFromNetwork(requestSender,
+ new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), port), network);
+ } catch (IOException e) {
+ Log.i(TAG, "Can't send packet to IPv6", e);
+ }
+ }
}
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index 185fac1..4e40efe 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -395,6 +395,7 @@
*/
public void removeService(int id) {
checkThread();
+ if (!mRegistrations.contains(id)) return;
if (DBG) {
Log.i(TAG, "Removing service with ID " + id);
}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java b/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java
index 91e08a8..7c84323 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsAnnouncer.java
@@ -22,10 +22,8 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.net.SocketAddress;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
/**
* Sends mDns announcements when a service registration changes and at regular intervals.
@@ -40,14 +38,12 @@
@NonNull
private final String mLogTag;
- static class AnnouncementInfo implements MdnsPacketRepeater.Request {
+ /** Announcement request to send with {@link MdnsAnnouncer}. */
+ public static class AnnouncementInfo implements MdnsPacketRepeater.Request {
@NonNull
private final MdnsPacket mPacket;
- @NonNull
- private final Supplier<Iterable<SocketAddress>> mDestinationsSupplier;
- AnnouncementInfo(List<MdnsRecord> announcedRecords, List<MdnsRecord> additionalRecords,
- Supplier<Iterable<SocketAddress>> destinationsSupplier) {
+ AnnouncementInfo(List<MdnsRecord> announcedRecords, List<MdnsRecord> additionalRecords) {
// Records to announce (as answers)
// Records to place in the "Additional records", with NSEC negative responses
// to mark records that have been verified unique
@@ -57,7 +53,6 @@
announcedRecords,
Collections.emptyList() /* authorityRecords */,
additionalRecords);
- mDestinationsSupplier = destinationsSupplier;
}
@Override
@@ -66,11 +61,6 @@
}
@Override
- public Iterable<SocketAddress> getDestinations(int index) {
- return mDestinationsSupplier.get();
- }
-
- @Override
public long getDelayMs(int nextIndex) {
// Delay is doubled for each announcement
return ANNOUNCEMENT_INITIAL_DELAY_MS << (nextIndex - 1);
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service/mdns/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index 0f3c23a..cc6b98b 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -34,18 +34,18 @@
* This class keeps tracking the set of registered {@link MdnsServiceBrowserListener} instances, and
* notify them when a mDNS service instance is found, updated, or removed?
*/
-public class MdnsDiscoveryManager implements MdnsSocketClient.Callback {
+public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
private static final String TAG = MdnsDiscoveryManager.class.getSimpleName();
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsDiscoveryManager");
private final ExecutorProvider executorProvider;
- private final MdnsSocketClient socketClient;
+ private final MdnsSocketClientBase socketClient;
private final Map<String, MdnsServiceTypeClient> serviceTypeClients = new ArrayMap<>();
- public MdnsDiscoveryManager(
- @NonNull ExecutorProvider executorProvider, @NonNull MdnsSocketClient socketClient) {
+ public MdnsDiscoveryManager(@NonNull ExecutorProvider executorProvider,
+ @NonNull MdnsSocketClientBase socketClient) {
this.executorProvider = executorProvider;
this.socketClient = socketClient;
}
@@ -76,12 +76,16 @@
return;
}
}
+ // Request the network for discovery.
+ socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork());
+
// All listeners of the same service types shares the same MdnsServiceTypeClient.
MdnsServiceTypeClient serviceTypeClient = serviceTypeClients.get(serviceType);
if (serviceTypeClient == null) {
serviceTypeClient = createServiceTypeClient(serviceType);
serviceTypeClients.put(serviceType, serviceTypeClient);
}
+ // TODO(b/264634275): Wait for a socket to be created before sending packets.
serviceTypeClient.startSendAndReceive(listener, searchOptions);
}
@@ -96,20 +100,22 @@
public synchronized void unregisterListener(
@NonNull String serviceType, @NonNull MdnsServiceBrowserListener listener) {
LOGGER.log("Unregistering listener for service type: %s", serviceType);
+ if (DBG) Log.d(TAG, "Unregistering listener for serviceType:" + serviceType);
MdnsServiceTypeClient serviceTypeClient = serviceTypeClients.get(serviceType);
if (serviceTypeClient == null) {
return;
}
if (serviceTypeClient.stopSendAndReceive(listener)) {
// No listener is registered for the service type anymore, remove it from the list of
- // the
- // service type clients.
+ // the service type clients.
serviceTypeClients.remove(serviceType);
if (serviceTypeClients.isEmpty()) {
// No discovery request. Stops the socket client.
socketClient.stopDiscovery();
}
}
+ // Unrequested the network.
+ socketClient.notifyNetworkUnrequested(listener);
}
@Override
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java b/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 644bdad..997dcbb 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -17,10 +17,17 @@
package com.android.server.connectivity.mdns;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.LinkAddress;
import android.net.nsd.NsdServiceInfo;
+import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.MdnsPacketRepeater.PacketRepeaterCallback;
+
+import java.io.IOException;
import java.util.List;
/**
@@ -28,6 +35,8 @@
*/
public class MdnsInterfaceAdvertiser {
private static final boolean DBG = MdnsAdvertiser.DBG;
+ @VisibleForTesting
+ public static final long EXIT_ANNOUNCEMENT_DELAY_MS = 100L;
@NonNull
private final String mTag;
@NonNull
@@ -35,7 +44,12 @@
@NonNull
private final AnnouncingCallback mAnnouncingCallback = new AnnouncingCallback();
@NonNull
+ private final MdnsRecordRepository mRecordRepository;
+ @NonNull
private final Callback mCb;
+ // Callbacks are on the same looper thread, but posted to the next handler loop
+ @NonNull
+ private final Handler mCbHandler;
@NonNull
private final MdnsInterfaceSocket mSocket;
@NonNull
@@ -75,10 +89,24 @@
* Callbacks from {@link MdnsProber}.
*/
private class ProbingCallback implements
- MdnsPacketRepeater.PacketRepeaterCallback<MdnsProber.ProbingInfo> {
+ PacketRepeaterCallback<MdnsProber.ProbingInfo> {
@Override
public void onFinished(MdnsProber.ProbingInfo info) {
- // TODO: probing finished, start announcements
+ final MdnsAnnouncer.AnnouncementInfo announcementInfo;
+ if (DBG) {
+ Log.v(mTag, "Probing finished for service " + info.getServiceId());
+ }
+ mCbHandler.post(() -> mCb.onRegisterServiceSucceeded(
+ MdnsInterfaceAdvertiser.this, info.getServiceId()));
+ try {
+ announcementInfo = mRecordRepository.onProbingSucceeded(info);
+ } catch (IOException e) {
+ Log.e(mTag, "Error building announcements", e);
+ return;
+ }
+
+ mAnnouncer.startSending(info.getServiceId(), announcementInfo,
+ 0L /* initialDelayMs */);
}
}
@@ -86,20 +114,64 @@
* Callbacks from {@link MdnsAnnouncer}.
*/
private class AnnouncingCallback
- implements MdnsPacketRepeater.PacketRepeaterCallback<MdnsAnnouncer.AnnouncementInfo> {
+ implements PacketRepeaterCallback<MdnsAnnouncer.AnnouncementInfo> {
// TODO: implement
}
+ /**
+ * Dependencies for {@link MdnsInterfaceAdvertiser}, useful for testing.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /** @see MdnsRecordRepository */
+ @NonNull
+ public MdnsRecordRepository makeRecordRepository(@NonNull Looper looper) {
+ return new MdnsRecordRepository(looper);
+ }
+
+ /** @see MdnsReplySender */
+ @NonNull
+ public MdnsReplySender makeReplySender(@NonNull Looper looper,
+ @NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer) {
+ return new MdnsReplySender(looper, socket, packetCreationBuffer);
+ }
+
+ /** @see MdnsAnnouncer */
+ public MdnsAnnouncer makeMdnsAnnouncer(@NonNull String interfaceTag, @NonNull Looper looper,
+ @NonNull MdnsReplySender replySender,
+ @Nullable PacketRepeaterCallback<MdnsAnnouncer.AnnouncementInfo> cb) {
+ return new MdnsAnnouncer(interfaceTag, looper, replySender, cb);
+ }
+
+ /** @see MdnsProber */
+ public MdnsProber makeMdnsProber(@NonNull String interfaceTag, @NonNull Looper looper,
+ @NonNull MdnsReplySender replySender,
+ @NonNull PacketRepeaterCallback<MdnsProber.ProbingInfo> cb) {
+ return new MdnsProber(interfaceTag, looper, replySender, cb);
+ }
+ }
+
public MdnsInterfaceAdvertiser(@NonNull String logTag,
@NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> initialAddresses,
@NonNull Looper looper, @NonNull byte[] packetCreationBuffer, @NonNull Callback cb) {
+ this(logTag, socket, initialAddresses, looper, packetCreationBuffer, cb,
+ new Dependencies());
+ }
+
+ public MdnsInterfaceAdvertiser(@NonNull String logTag,
+ @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> initialAddresses,
+ @NonNull Looper looper, @NonNull byte[] packetCreationBuffer, @NonNull Callback cb,
+ @NonNull Dependencies deps) {
mTag = MdnsInterfaceAdvertiser.class.getSimpleName() + "/" + logTag;
+ mRecordRepository = deps.makeRecordRepository(looper);
+ mRecordRepository.updateAddresses(initialAddresses);
mSocket = socket;
mCb = cb;
- mReplySender = new MdnsReplySender(looper, socket, packetCreationBuffer);
- mAnnouncer = new MdnsAnnouncer(logTag, looper, mReplySender,
+ mCbHandler = new Handler(looper);
+ mReplySender = deps.makeReplySender(looper, socket, packetCreationBuffer);
+ mAnnouncer = deps.makeMdnsAnnouncer(logTag, looper, mReplySender,
mAnnouncingCallback);
- mProber = new MdnsProber(logTag, looper, mReplySender, mProbingCallback);
+ mProber = deps.makeMdnsProber(logTag, looper, mReplySender, mProbingCallback);
}
/**
@@ -110,7 +182,7 @@
* {@link #destroyNow()}.
*/
public void start() {
- // TODO: implement
+ // TODO: start receiving packets
}
/**
@@ -119,7 +191,17 @@
* @throws NameConflictException There is already a service being advertised with that name.
*/
public void addService(int id, NsdServiceInfo service) throws NameConflictException {
- // TODO: implement
+ final int replacedExitingService = mRecordRepository.addService(id, service);
+ // Cancel announcements for the existing service. This only happens for exiting services
+ // (so cancelling exiting announcements), as per RecordRepository.addService.
+ if (replacedExitingService >= 0) {
+ if (DBG) {
+ Log.d(mTag, "Service " + replacedExitingService
+ + " getting re-added, cancelling exit announcements");
+ }
+ mAnnouncer.stop(replacedExitingService);
+ }
+ mProber.startProbing(mRecordRepository.setServiceProbing(id));
}
/**
@@ -128,7 +210,25 @@
* This will trigger exit announcements for the service.
*/
public void removeService(int id) {
- // TODO: implement
+ mProber.stop(id);
+ mAnnouncer.stop(id);
+ final MdnsAnnouncer.AnnouncementInfo exitInfo = mRecordRepository.exitService(id);
+ if (exitInfo != null) {
+ // This effectively schedules destroyNow(), as it is to be called when the exit
+ // announcement finishes if there is no service left.
+ // A non-zero exit announcement delay follows legacy mdnsresponder behavior, and is
+ // also useful to ensure that when a host receives the exit announcement, the service
+ // has been unregistered on all interfaces; so an announcement sent from interface A
+ // that was already in-flight while unregistering won't be received after the exit on
+ // interface B.
+ mAnnouncer.startSending(id, exitInfo, EXIT_ANNOUNCEMENT_DELAY_MS);
+ } else {
+ // No exit announcement necessary: remove the service immediately.
+ mRecordRepository.removeService(id);
+ if (mRecordRepository.getServicesCount() == 0) {
+ destroyNow();
+ }
+ }
}
/**
@@ -137,7 +237,9 @@
* This causes new address records to be announced.
*/
public void updateAddresses(@NonNull List<LinkAddress> newAddresses) {
- // TODO: implement
+ mRecordRepository.updateAddresses(newAddresses);
+ // TODO: restart advertising, but figure out what exit messages need to be sent for the
+ // previous addresses
}
/**
@@ -146,7 +248,13 @@
* <p>Useful when the underlying network went away. This will trigger an onDestroyed callback.
*/
public void destroyNow() {
- // TODO: implement
+ for (int serviceId : mRecordRepository.clearServices()) {
+ mProber.stop(serviceId);
+ mAnnouncer.stop(serviceId);
+ }
+
+ // TODO: stop receiving packets
+ mCbHandler.post(() -> mCb.onDestroyed(mSocket));
}
/**
@@ -169,7 +277,6 @@
* Also returns false if the specified service is not registered.
*/
public boolean isProbing(int serviceId) {
- // TODO: implement
- return true;
+ return mRecordRepository.isProbing(serviceId);
}
}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java b/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
index 67c893d..d1290b6 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
@@ -22,10 +22,15 @@
import android.annotation.NonNull;
import android.net.LinkAddress;
import android.net.util.SocketUtils;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
@@ -41,15 +46,19 @@
* otherwise.
*
* @see MulticastSocket for javadoc of each public method.
+ * @see MulticastSocket for javadoc of each public method.
*/
public class MdnsInterfaceSocket {
private static final String TAG = MdnsInterfaceSocket.class.getSimpleName();
@NonNull private final MulticastSocket mMulticastSocket;
@NonNull private final NetworkInterface mNetworkInterface;
+ @NonNull private final MulticastPacketReader mPacketReader;
+ @NonNull private final ParcelFileDescriptor mFileDescriptor;
private boolean mJoinedIpv4 = false;
private boolean mJoinedIpv6 = false;
- public MdnsInterfaceSocket(@NonNull NetworkInterface networkInterface, int port)
+ public MdnsInterfaceSocket(@NonNull NetworkInterface networkInterface, int port,
+ @NonNull Looper looper, @NonNull byte[] packetReadBuffer)
throws IOException {
mNetworkInterface = networkInterface;
mMulticastSocket = new MulticastSocket(port);
@@ -58,11 +67,19 @@
mMulticastSocket.setNetworkInterface(networkInterface);
// Bind socket to the interface for receiving from that interface only.
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(mMulticastSocket)) {
- SocketUtils.bindSocketToInterface(pfd.getFileDescriptor(), mNetworkInterface.getName());
+ mFileDescriptor = ParcelFileDescriptor.fromDatagramSocket(mMulticastSocket);
+ try {
+ final FileDescriptor fd = mFileDescriptor.getFileDescriptor();
+ final int flags = Os.fcntlInt(fd, OsConstants.F_GETFL, 0);
+ Os.fcntlInt(fd, OsConstants.F_SETFL, flags | OsConstants.SOCK_NONBLOCK);
+ SocketUtils.bindSocketToInterface(fd, mNetworkInterface.getName());
} catch (ErrnoException e) {
throw new IOException("Error setting socket options", e);
}
+
+ mPacketReader = new MulticastPacketReader(networkInterface.getName(), mFileDescriptor,
+ new Handler(looper), packetReadBuffer);
+ mPacketReader.start();
}
/**
@@ -74,23 +91,14 @@
mMulticastSocket.send(packet);
}
- /**
- * Receives a datagram packet from this socket.
- *
- * <p>This method could be used on any thread.
- */
- public void receive(@NonNull DatagramPacket packet) throws IOException {
- mMulticastSocket.receive(packet);
- }
-
- private boolean hasIpv4Address(List<LinkAddress> addresses) {
+ private static boolean hasIpv4Address(@NonNull List<LinkAddress> addresses) {
for (LinkAddress address : addresses) {
if (address.isIpv4()) return true;
}
return false;
}
- private boolean hasIpv6Address(List<LinkAddress> addresses) {
+ private static boolean hasIpv6Address(@NonNull List<LinkAddress> addresses) {
for (LinkAddress address : addresses) {
if (address.isIpv6()) return true;
}
@@ -103,7 +111,7 @@
maybeJoinIpv6(addresses);
}
- private boolean joinGroup(InetSocketAddress multicastAddress) {
+ private boolean joinGroup(@NonNull InetSocketAddress multicastAddress) {
try {
mMulticastSocket.joinGroup(multicastAddress, mNetworkInterface);
return true;
@@ -114,7 +122,7 @@
}
}
- private void maybeJoinIpv4(List<LinkAddress> addresses) {
+ private void maybeJoinIpv4(@NonNull List<LinkAddress> addresses) {
final boolean hasAddr = hasIpv4Address(addresses);
if (!mJoinedIpv4 && hasAddr) {
mJoinedIpv4 = joinGroup(MULTICAST_IPV4_ADDRESS);
@@ -124,7 +132,7 @@
}
}
- private void maybeJoinIpv6(List<LinkAddress> addresses) {
+ private void maybeJoinIpv6(@NonNull List<LinkAddress> addresses) {
final boolean hasAddr = hasIpv6Address(addresses);
if (!mJoinedIpv6 && hasAddr) {
mJoinedIpv6 = joinGroup(MULTICAST_IPV6_ADDRESS);
@@ -134,26 +142,26 @@
}
}
- /*** Destroy this socket by leaving all joined multicast groups and closing this socket. */
+ /*** Destroy the socket */
public void destroy() {
- if (mJoinedIpv4) {
- try {
- mMulticastSocket.leaveGroup(MULTICAST_IPV4_ADDRESS, mNetworkInterface);
- } catch (IOException e) {
- Log.e(TAG, "Error leaving IPv4 group for " + mNetworkInterface, e);
- }
- }
- if (mJoinedIpv6) {
- try {
- mMulticastSocket.leaveGroup(MULTICAST_IPV6_ADDRESS, mNetworkInterface);
- } catch (IOException e) {
- Log.e(TAG, "Error leaving IPv4 group for " + mNetworkInterface, e);
- }
+ mPacketReader.stop();
+ try {
+ mFileDescriptor.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Close file descriptor failed.");
}
mMulticastSocket.close();
}
/**
+ * Add a handler to receive callbacks when reads the packet from socket. If the handler is
+ * already set, this is a no-op.
+ */
+ public void addPacketHandler(@NonNull MulticastPacketReader.PacketHandler handler) {
+ mPacketReader.addPacketHandler(handler);
+ }
+
+ /**
* Returns the network interface that this socket is bound to.
*
* <p>This method could be used on any thread.
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service/mdns/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
new file mode 100644
index 0000000..d959065
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
+import static com.android.server.connectivity.mdns.MdnsSocketProvider.isNetworkMatched;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
+ *
+ * * <p>This class is not thread safe.
+ */
+public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
+ private static final String TAG = MdnsMultinetworkSocketClient.class.getSimpleName();
+ private static final boolean DBG = MdnsDiscoveryManager.DBG;
+
+ @NonNull private final Handler mHandler;
+ @NonNull private final MdnsSocketProvider mSocketProvider;
+ @NonNull private final MdnsResponseDecoder mResponseDecoder;
+
+ private final Map<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
+ new ArrayMap<>();
+ private final ArrayMap<MdnsInterfaceSocket, Network> mActiveNetworkSockets = new ArrayMap<>();
+ private final ArrayMap<MdnsInterfaceSocket, ReadPacketHandler> mSocketPacketHandlers =
+ new ArrayMap<>();
+ private MdnsSocketClientBase.Callback mCallback = null;
+ private int mReceivedPacketNumber = 0;
+
+ public MdnsMultinetworkSocketClient(@NonNull Looper looper,
+ @NonNull MdnsSocketProvider provider) {
+ mHandler = new Handler(looper);
+ mSocketProvider = provider;
+ mResponseDecoder = new MdnsResponseDecoder(
+ new MdnsResponseDecoder.Clock(), null /* serviceType */);
+ }
+
+ private class InterfaceSocketCallback implements MdnsSocketProvider.SocketCallback {
+ @Override
+ public void onSocketCreated(@NonNull Network network,
+ @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {
+ // The socket may be already created by other request before, try to get the stored
+ // ReadPacketHandler.
+ ReadPacketHandler handler = mSocketPacketHandlers.get(socket);
+ if (handler == null) {
+ // First request to create this socket. Initial a ReadPacketHandler for this socket.
+ handler = new ReadPacketHandler(network, socket.getInterface().getIndex());
+ mSocketPacketHandlers.put(socket, handler);
+ }
+ socket.addPacketHandler(handler);
+ mActiveNetworkSockets.put(socket, network);
+ }
+
+ @Override
+ public void onInterfaceDestroyed(@NonNull Network network,
+ @NonNull MdnsInterfaceSocket socket) {
+ mSocketPacketHandlers.remove(socket);
+ mActiveNetworkSockets.remove(socket);
+ }
+ }
+
+ private class ReadPacketHandler implements MulticastPacketReader.PacketHandler {
+ private final Network mNetwork;
+ private final int mInterfaceIndex;
+
+ ReadPacketHandler(@NonNull Network network, int interfaceIndex) {
+ mNetwork = network;
+ mInterfaceIndex = interfaceIndex;
+ }
+
+ @Override
+ public void handlePacket(byte[] recvbuf, int length, InetSocketAddress src) {
+ processResponsePacket(recvbuf, length, mInterfaceIndex, mNetwork);
+ }
+ }
+
+ /*** Set callback for receiving mDns response */
+ @Override
+ public void setCallback(@Nullable MdnsSocketClientBase.Callback callback) {
+ ensureRunningOnHandlerThread(mHandler);
+ mCallback = callback;
+ }
+
+ /***
+ * Notify that the given network is requested for mdns discovery / resolution
+ *
+ * @param listener the listener for discovery.
+ * @param network the target network for discovery. Null means discovery on all possible
+ * interfaces.
+ */
+ @Override
+ public void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
+ @Nullable Network network) {
+ ensureRunningOnHandlerThread(mHandler);
+ InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
+ if (callback != null) {
+ throw new IllegalArgumentException("Can not register duplicated listener");
+ }
+
+ if (DBG) Log.d(TAG, "notifyNetworkRequested: network=" + network);
+ callback = new InterfaceSocketCallback();
+ mRequestedNetworks.put(listener, callback);
+ mSocketProvider.requestSocket(network, callback);
+ }
+
+ /*** Notify that the network is unrequested */
+ @Override
+ public void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) {
+ ensureRunningOnHandlerThread(mHandler);
+ final InterfaceSocketCallback callback = mRequestedNetworks.remove(listener);
+ if (callback == null) {
+ Log.e(TAG, "Can not be unrequested with unknown listener=" + listener);
+ return;
+ }
+ mSocketProvider.unrequestSocket(callback);
+ }
+
+ private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork) {
+ final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
+ instanceof Inet6Address;
+ final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
+ instanceof Inet4Address;
+ for (int i = 0; i < mActiveNetworkSockets.size(); i++) {
+ final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
+ final Network network = mActiveNetworkSockets.valueAt(i);
+ // Check ip capability and network before sending packet
+ if (((isIpv6 && socket.hasJoinedIpv6()) || (isIpv4 && socket.hasJoinedIpv4()))
+ && isNetworkMatched(targetNetwork, network)) {
+ try {
+ socket.send(packet);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to send a mDNS packet.", e);
+ }
+ }
+ }
+ }
+
+ private void processResponsePacket(byte[] recvbuf, int length, int interfaceIndex,
+ @NonNull Network network) {
+ int packetNumber = ++mReceivedPacketNumber;
+
+ final List<MdnsResponse> responses = new ArrayList<>();
+ final int errorCode = mResponseDecoder.decode(
+ recvbuf, length, responses, interfaceIndex, network);
+ if (errorCode == MdnsResponseDecoder.SUCCESS) {
+ for (MdnsResponse response : responses) {
+ if (mCallback != null) {
+ mCallback.onResponseReceived(response);
+ }
+ }
+ } else if (errorCode != MdnsResponseErrorCode.ERROR_NOT_RESPONSE_MESSAGE) {
+ if (mCallback != null) {
+ mCallback.onFailedToParseMdnsResponse(packetNumber, errorCode);
+ }
+ }
+ }
+
+ /** Sends a mDNS request packet that asks for multicast response. */
+ @Override
+ public void sendMulticastPacket(@NonNull DatagramPacket packet) {
+ sendMulticastPacket(packet, null /* network */);
+ }
+
+ /**
+ * Sends a mDNS request packet via given network that asks for multicast response. Null network
+ * means sending packet via all networks.
+ */
+ @Override
+ public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ mHandler.post(() -> sendMdnsPacket(packet, network));
+ }
+
+ /** Sends a mDNS request packet that asks for unicast response. */
+ @Override
+ public void sendUnicastPacket(@NonNull DatagramPacket packet) {
+ sendUnicastPacket(packet, null /* network */);
+ }
+
+ /**
+ * Sends a mDNS request packet via given network that asks for unicast response. Null network
+ * means sending packet via all networks.
+ */
+ @Override
+ public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ // TODO: Separate unicast packet.
+ mHandler.post(() -> sendMdnsPacket(packet, network));
+ }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
index 856a2cd..aa38844 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
@@ -38,8 +38,13 @@
/** Constructs a reader for the given packet. */
public MdnsPacketReader(DatagramPacket packet) {
- buf = packet.getData();
- count = packet.getLength();
+ this(packet.getData(), packet.getLength());
+ }
+
+ /** Constructs a reader for the given packet. */
+ public MdnsPacketReader(byte[] buffer, int length) {
+ buf = buffer;
+ count = length;
pos = 0;
limit = -1;
labelDictionary = new SparseArray<>(16);
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
index 015dbd8..ae54e70 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
@@ -24,7 +24,7 @@
import android.util.Log;
import java.io.IOException;
-import java.net.SocketAddress;
+import java.net.InetSocketAddress;
/**
* A class used to send several packets at given time intervals.
@@ -32,6 +32,14 @@
*/
public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
private static final boolean DBG = MdnsAdvertiser.DBG;
+ private static final InetSocketAddress IPV4_ADDR = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
+ private static final InetSocketAddress IPV6_ADDR = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
+ private static final InetSocketAddress[] ALL_ADDRS = new InetSocketAddress[] {
+ IPV4_ADDR, IPV6_ADDR
+ };
+
@NonNull
private final MdnsReplySender mReplySender;
@NonNull
@@ -70,12 +78,6 @@
MdnsPacket getPacket(int index);
/**
- * Get a set of destinations for the packet for one iteration.
- */
- @NonNull
- Iterable<SocketAddress> getDestinations(int index);
-
- /**
* Get the delay in milliseconds until the next packet transmission.
*/
long getDelayMs(int nextIndex);
@@ -110,12 +112,13 @@
}
final MdnsPacket packet = request.getPacket(index);
- final Iterable<SocketAddress> destinations = request.getDestinations(index);
if (DBG) {
- Log.v(getTag(), "Sending packets to " + destinations + " for iteration "
- + index + " out of " + request.getNumSends());
+ Log.v(getTag(), "Sending packets for iteration " + index + " out of "
+ + request.getNumSends());
}
- for (SocketAddress destination : destinations) {
+ // Send to both v4 and v6 addresses; the reply sender will take care of ignoring the
+ // send when the socket has not joined the relevant group.
+ for (InetSocketAddress destination : ALL_ADDRS) {
try {
mReplySender.sendNow(packet, destination);
} catch (IOException e) {
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java b/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java
index db7049e..2cd9148 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsProber.java
@@ -22,12 +22,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
-import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
/**
* Sends mDns probe requests to verify service records are unique on the network.
@@ -46,26 +44,21 @@
mLogTag = MdnsProber.class.getSimpleName() + "/" + interfaceTag;
}
- static class ProbingInfo implements Request {
+ /** Probing request to send with {@link MdnsProber}. */
+ public static class ProbingInfo implements Request {
private final int mServiceId;
@NonNull
private final MdnsPacket mPacket;
- @NonNull
- private final Supplier<Iterable<SocketAddress>> mDestinationsSupplier;
/**
* Create a new ProbingInfo
* @param serviceId Service to probe for.
* @param probeRecords Records to be probed for uniqueness.
- * @param destinationsSupplier Supplier for the probe destinations. Will be called on the
- * probe handler thread for each probe.
*/
- ProbingInfo(int serviceId, @NonNull List<MdnsRecord> probeRecords,
- @NonNull Supplier<Iterable<SocketAddress>> destinationsSupplier) {
+ ProbingInfo(int serviceId, @NonNull List<MdnsRecord> probeRecords) {
mServiceId = serviceId;
mPacket = makePacket(probeRecords);
- mDestinationsSupplier = destinationsSupplier;
}
public int getServiceId() {
@@ -78,12 +71,6 @@
return mPacket;
}
- @NonNull
- @Override
- public Iterable<SocketAddress> getDestinations(int index) {
- return mDestinationsSupplier.get();
- }
-
@Override
public long getDelayMs(int nextIndex) {
// As per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service/mdns/com/android/server/connectivity/mdns/MdnsRecordRepository.java
new file mode 100644
index 0000000..bb9c751
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkAddress;
+import android.net.nsd.NsdServiceInfo;
+import android.os.Looper;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A repository of records advertised through {@link MdnsInterfaceAdvertiser}.
+ *
+ * Must be used on a consistent looper thread.
+ */
+public class MdnsRecordRepository {
+ // TTLs as per RFC6762 10.
+ // TTL for records with a host name as the resource record's name (e.g., A, AAAA, HINFO) or a
+ // host name contained within the resource record's rdata (e.g., SRV, reverse mapping PTR
+ // record)
+ private static final long NAME_RECORDS_TTL_MILLIS = TimeUnit.SECONDS.toMillis(120);
+ // TTL for other records
+ private static final long NON_NAME_RECORDS_TTL_MILLIS = TimeUnit.MINUTES.toMillis(75);
+
+ // Top-level domain for link-local queries, as per RFC6762 3.
+ private static final String LOCAL_TLD = "local";
+
+ // Service type for service enumeration (RFC6763 9.)
+ private static final String[] DNS_SD_SERVICE_TYPE =
+ new String[] { "_services", "_dns-sd", "_udp", LOCAL_TLD };
+
+ // Map of service unique ID -> records for service
+ @NonNull
+ private final SparseArray<ServiceRegistration> mServices = new SparseArray<>();
+ @NonNull
+ private final Looper mLooper;
+ @NonNull
+ private String[] mDeviceHostname;
+
+ public MdnsRecordRepository(@NonNull Looper looper) {
+ this(looper, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public MdnsRecordRepository(@NonNull Looper looper, @NonNull Dependencies deps) {
+ mDeviceHostname = deps.getHostname();
+ mLooper = looper;
+ }
+
+ /**
+ * Dependencies to use with {@link MdnsRecordRepository}, useful for testing.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get a unique hostname to be used by the device.
+ */
+ @NonNull
+ public String[] getHostname() {
+ // Generate a very-probably-unique hostname. This allows minimizing possible conflicts
+ // to the point that probing for it is no longer necessary (as per RFC6762 8.1 last
+ // paragraph), and does not leak more information than what could already be obtained by
+ // looking at the mDNS packets source address.
+ // This differs from historical behavior that just used "Android.local" for many
+ // devices, creating a lot of conflicts.
+ // Having a different hostname per interface is an acceptable option as per RFC6762 14.
+ // This hostname will change every time the interface is reconnected, so this does not
+ // allow tracking the device.
+ // TODO: consider deriving a hostname from other sources, such as the IPv6 addresses
+ // (reusing the same privacy-protecting mechanics).
+ return new String[] {
+ "Android_" + UUID.randomUUID().toString().replace("-", ""), LOCAL_TLD };
+ }
+
+ /**
+ * @see NetworkInterface#getInetAddresses().
+ */
+ @NonNull
+ public Enumeration<InetAddress> getInterfaceInetAddresses(@NonNull NetworkInterface iface) {
+ return iface.getInetAddresses();
+ }
+ }
+
+ private static class RecordInfo<T extends MdnsRecord> {
+ public final T record;
+ public final NsdServiceInfo serviceInfo;
+
+ /**
+ * Whether the name of this record is expected to be fully owned by the service or may be
+ * advertised by other hosts as well (shared).
+ */
+ public final boolean isSharedName;
+
+ /**
+ * Whether probing is still in progress for the record.
+ */
+ public boolean isProbing;
+
+ RecordInfo(NsdServiceInfo serviceInfo, T record, boolean sharedName,
+ boolean probing) {
+ this.serviceInfo = serviceInfo;
+ this.record = record;
+ this.isSharedName = sharedName;
+ this.isProbing = probing;
+ }
+ }
+
+ private static class ServiceRegistration {
+ @NonNull
+ public final List<RecordInfo<?>> allRecords;
+ @NonNull
+ public final RecordInfo<MdnsPointerRecord> ptrRecord;
+ @NonNull
+ public final RecordInfo<MdnsServiceRecord> srvRecord;
+ @NonNull
+ public final RecordInfo<MdnsTextRecord> txtRecord;
+ @NonNull
+ public final NsdServiceInfo serviceInfo;
+
+ /**
+ * Whether the service is sending exit announcements and will be destroyed soon.
+ */
+ public boolean exiting = false;
+
+ /**
+ * 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) {
+ this.serviceInfo = serviceInfo;
+
+ final String[] serviceType = splitServiceType(serviceInfo);
+ final String[] serviceName = splitFullyQualifiedName(serviceInfo, serviceType);
+
+ // Service PTR record
+ ptrRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsPointerRecord(
+ serviceType,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ NON_NAME_RECORDS_TTL_MILLIS,
+ serviceName),
+ true /* sharedName */, true /* probing */);
+
+ srvRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsServiceRecord(serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ NAME_RECORDS_TTL_MILLIS, 0 /* servicePriority */, 0 /* serviceWeight */,
+ serviceInfo.getPort(),
+ deviceHostname),
+ false /* sharedName */, true /* probing */);
+
+ txtRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsTextRecord(serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */, // Service name is verified unique after probing
+ NON_NAME_RECORDS_TTL_MILLIS,
+ attrsToTextEntries(serviceInfo.getAttributes())),
+ false /* sharedName */, true /* probing */);
+
+ final ArrayList<RecordInfo<?>> allRecords = new ArrayList<>(4);
+ allRecords.add(ptrRecord);
+ allRecords.add(srvRecord);
+ allRecords.add(txtRecord);
+ // Service type enumeration record (RFC6763 9.)
+ allRecords.add(new RecordInfo<>(
+ serviceInfo,
+ new MdnsPointerRecord(
+ DNS_SD_SERVICE_TYPE,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ NON_NAME_RECORDS_TTL_MILLIS,
+ serviceType),
+ true /* sharedName */, true /* probing */));
+
+ this.allRecords = Collections.unmodifiableList(allRecords);
+ }
+
+ void setProbing(boolean probing) {
+ for (RecordInfo<?> info : allRecords) {
+ info.isProbing = probing;
+ }
+ }
+ }
+
+ /**
+ * Inform the repository of the latest interface addresses.
+ */
+ public void updateAddresses(@NonNull List<LinkAddress> newAddresses) {
+ // TODO: implement to update addresses in records
+ }
+
+ /**
+ * Add a service to the repository.
+ *
+ * This may remove/replace any existing service that used the name added but is exiting.
+ * @param serviceId A unique service ID.
+ * @param serviceInfo Service info to add.
+ * @return If the added service replaced another with a matching name (which was exiting), the
+ * ID of the replaced service.
+ * @throws NameConflictException There is already a (non-exiting) service using the name.
+ */
+ public int addService(int serviceId, NsdServiceInfo serviceInfo) throws NameConflictException {
+ if (mServices.contains(serviceId)) {
+ throw new IllegalArgumentException(
+ "Service ID must not be reused across registrations: " + serviceId);
+ }
+
+ final int existing = getServiceByName(serviceInfo.getServiceName());
+ // It's OK to re-add a service that is exiting
+ if (existing >= 0 && !mServices.get(existing).exiting) {
+ throw new NameConflictException(existing);
+ }
+
+ final ServiceRegistration registration = new ServiceRegistration(
+ mDeviceHostname, serviceInfo);
+ mServices.put(serviceId, registration);
+
+ // Remove existing exiting service
+ mServices.remove(existing);
+ return existing;
+ }
+
+ /**
+ * @return The ID of the service identified by its name, or -1 if none.
+ */
+ private int getServiceByName(@NonNull String serviceName) {
+ for (int i = 0; i < mServices.size(); i++) {
+ final ServiceRegistration registration = mServices.valueAt(i);
+ if (serviceName.equals(registration.serviceInfo.getServiceName())) {
+ return mServices.keyAt(i);
+ }
+ }
+ return -1;
+ }
+
+ private MdnsProber.ProbingInfo makeProbingInfo(int serviceId,
+ @NonNull MdnsServiceRecord srvRecord) {
+ final List<MdnsRecord> probingRecords = new ArrayList<>();
+ // Probe with cacheFlush cleared; it is set when announcing, as it was verified unique:
+ // RFC6762 10.2
+ probingRecords.add(new MdnsServiceRecord(srvRecord.getName(),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ srvRecord.getTtl(),
+ srvRecord.getServicePriority(), srvRecord.getServiceWeight(),
+ srvRecord.getServicePort(),
+ srvRecord.getServiceHost()));
+
+ return new MdnsProber.ProbingInfo(serviceId, probingRecords);
+ }
+
+ private static List<MdnsServiceInfo.TextEntry> attrsToTextEntries(Map<String, byte[]> attrs) {
+ final List<MdnsServiceInfo.TextEntry> out = new ArrayList<>(attrs.size());
+ for (Map.Entry<String, byte[]> attr : attrs.entrySet()) {
+ out.add(new MdnsServiceInfo.TextEntry(attr.getKey(), attr.getValue()));
+ }
+ return out;
+ }
+
+ /**
+ * Mark a service in the repository as exiting.
+ * @param id ID of the service, used at registration time.
+ * @return The exit announcement to indicate the service was removed, or null if not necessary.
+ */
+ @Nullable
+ public MdnsAnnouncer.AnnouncementInfo exitService(int id) {
+ final ServiceRegistration registration = mServices.get(id);
+ if (registration == null) return null;
+ if (registration.exiting) return null;
+
+ registration.exiting = true;
+
+ // TODO: implement
+ return null;
+ }
+
+ /**
+ * Remove a service from the repository
+ */
+ public void removeService(int id) {
+ mServices.remove(id);
+ }
+
+ /**
+ * @return The number of services currently held in the repository, including exiting services.
+ */
+ public int getServicesCount() {
+ return mServices.size();
+ }
+
+ /**
+ * Remove all services from the repository
+ * @return IDs of the removed services
+ */
+ @NonNull
+ public int[] clearServices() {
+ final int[] ret = new int[mServices.size()];
+ for (int i = 0; i < mServices.size(); i++) {
+ ret[i] = mServices.keyAt(i);
+ }
+ mServices.clear();
+ return ret;
+ }
+
+ /**
+ * Called to indicate that probing succeeded for a service.
+ * @param probeSuccessInfo The successful probing info.
+ * @return The {@link MdnsAnnouncer.AnnouncementInfo} to send, now that probing has succeeded.
+ */
+ public MdnsAnnouncer.AnnouncementInfo onProbingSucceeded(
+ MdnsProber.ProbingInfo probeSuccessInfo) throws IOException {
+ // TODO: implement: set service as not probing anymore and generate announcements
+ throw new IOException("Announcements not implemented");
+ }
+
+ /**
+ * (Re)set a service to the probing state.
+ * @return The {@link MdnsProber.ProbingInfo} to send for probing.
+ */
+ @Nullable
+ public MdnsProber.ProbingInfo setServiceProbing(int serviceId) {
+ final ServiceRegistration registration = mServices.get(serviceId);
+ if (registration == null) return null;
+
+ registration.setProbing(true);
+ return makeProbingInfo(serviceId, registration.srvRecord.record);
+ }
+
+ /**
+ * Indicates whether a given service is in probing state.
+ */
+ public boolean isProbing(int serviceId) {
+ final ServiceRegistration registration = mServices.get(serviceId);
+ if (registration == null) return false;
+
+ return registration.srvRecord.isProbing;
+ }
+
+ private static String[] splitFullyQualifiedName(
+ @NonNull NsdServiceInfo info, @NonNull String[] serviceType) {
+ final String[] split = new String[serviceType.length + 1];
+ split[0] = info.getServiceName();
+ System.arraycopy(serviceType, 0, split, 1, serviceType.length);
+
+ return split;
+ }
+
+ private static String[] splitServiceType(@NonNull NsdServiceInfo info) {
+ // String.split(pattern, 0) removes trailing empty strings, which would appear when
+ // splitting "domain.name." (with a dot a the end), so this is what is needed here.
+ final String[] split = info.getServiceType().split("\\.", 0);
+ final String[] type = new String[split.length + 1];
+ System.arraycopy(split, 0, type, 0, split.length);
+ type[split.length] = LOCAL_TLD;
+
+ return type;
+ }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
index adf6f4d..c6b8f47 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -21,8 +21,10 @@
import java.io.IOException;
import java.net.DatagramPacket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
import java.net.MulticastSocket;
-import java.net.SocketAddress;
/**
* A class that handles sending mDNS replies to a {@link MulticastSocket}, possibly queueing them
@@ -50,11 +52,16 @@
*
* Must be called on the looper thread used by the {@link MdnsReplySender}.
*/
- public void sendNow(@NonNull MdnsPacket packet, @NonNull SocketAddress destination)
+ public void sendNow(@NonNull MdnsPacket packet, @NonNull InetSocketAddress destination)
throws IOException {
if (Thread.currentThread() != mLooper.getThread()) {
throw new IllegalStateException("sendNow must be called in the handler thread");
}
+ if (!((destination.getAddress() instanceof Inet6Address && mSocket.hasJoinedIpv6())
+ || (destination.getAddress() instanceof Inet4Address && mSocket.hasJoinedIpv4()))) {
+ // Skip sending if the socket has not joined the v4/v6 group (there was no address)
+ return;
+ }
// TODO: support packets over size (send in multiple packets with TC bit set)
final MdnsPacketWriter writer = new MdnsPacketWriter(mPacketCreationBuffer);
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 7cf84f6..50f2069 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -101,7 +101,24 @@
*/
public int decode(@NonNull DatagramPacket packet, @NonNull List<MdnsResponse> responses,
int interfaceIndex, @Nullable Network network) {
- MdnsPacketReader reader = new MdnsPacketReader(packet);
+ return decode(packet.getData(), packet.getLength(), responses, interfaceIndex, network);
+ }
+
+ /**
+ * Decodes all mDNS responses for the desired service type from a packet. The class does not
+ * check
+ * the responses for completeness; the caller should do that.
+ *
+ * @param recvbuf The received data buffer to read from.
+ * @param length The length of received data buffer.
+ * @param interfaceIndex the network interface index (or {@link
+ * MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if not known) at which the packet was received
+ * @param network the network at which the packet was received, or null if it is unknown.
+ * @return A list of mDNS responses, or null if the packet contained no appropriate responses.
+ */
+ public int decode(@NonNull byte[] recvbuf, int length, @NonNull List<MdnsResponse> responses,
+ int interfaceIndex, @Nullable Network network) {
+ MdnsPacketReader reader = new MdnsPacketReader(recvbuf, length);
List<MdnsRecord> records;
try {
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
index fcf9058..73a7e3a 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
@@ -35,4 +35,6 @@
public static final int ERROR_READING_TXT_RDATA = 10;
public static final int ERROR_SKIPPING_UNKNOWN_RECORD = 11;
public static final int ERROR_END_OF_FILE = 12;
+ public static final int ERROR_READING_NSEC_RDATA = 13;
+ public static final int ERROR_READING_ANY_RDATA = 14;
}
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index 538f376..d26fbdb 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Network;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -52,7 +53,7 @@
private final String serviceType;
private final String[] serviceTypeLabels;
- private final MdnsSocketClient socketClient;
+ private final MdnsSocketClientBase socketClient;
private final ScheduledExecutorService executor;
private final Object lock = new Object();
private final Set<MdnsServiceBrowserListener> listeners = new ArraySet<>();
@@ -77,11 +78,11 @@
* Constructor of {@link MdnsServiceTypeClient}.
*
* @param socketClient Sends and receives mDNS packet.
- * @param executor A {@link ScheduledExecutorService} used to schedule query tasks.
+ * @param executor A {@link ScheduledExecutorService} used to schedule query tasks.
*/
public MdnsServiceTypeClient(
@NonNull String serviceType,
- @NonNull MdnsSocketClient socketClient,
+ @NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor) {
this.serviceType = serviceType;
this.socketClient = socketClient;
@@ -169,7 +170,8 @@
new QueryTaskConfig(
searchOptions.getSubtypes(),
searchOptions.isPassiveMode(),
- ++currentSessionId)));
+ ++currentSessionId,
+ searchOptions.getNetwork())));
}
}
@@ -322,9 +324,10 @@
private int burstCounter;
private int timeToRunNextTaskInMs;
private boolean isFirstBurst;
+ @Nullable private final Network network;
QueryTaskConfig(@NonNull Collection<String> subtypes, boolean usePassiveMode,
- long sessionId) {
+ long sessionId, @Nullable Network network) {
this.usePassiveMode = usePassiveMode;
this.subtypes = new ArrayList<>(subtypes);
this.queriesPerBurst = QUERIES_PER_BURST;
@@ -346,6 +349,7 @@
// doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
}
+ this.network = network;
}
QueryTaskConfig getConfigForNextRun() {
@@ -405,7 +409,8 @@
serviceType,
config.subtypes,
config.expectUnicastResponse,
- config.transactionId)
+ config.transactionId,
+ config.network)
.call();
} catch (RuntimeException e) {
LOGGER.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
index 64c4495..5fd1354 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
@@ -40,9 +40,9 @@
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsSocket");
static final int INTERFACE_INDEX_UNSPECIFIED = -1;
- protected static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
+ public static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
- protected static final InetSocketAddress MULTICAST_IPV6_ADDRESS =
+ public static final InetSocketAddress MULTICAST_IPV6_ADDRESS =
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider;
private final MulticastSocket multicastSocket;
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 6a321d1..907687e 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsSocketClientBase.Callback;
+
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,7 +49,7 @@
*
* <p>See https://tools.ietf.org/html/rfc6763 (namely sections 4 and 5).
*/
-public class MdnsSocketClient {
+public class MdnsSocketClient implements MdnsSocketClientBase {
private static final String TAG = "MdnsClient";
// TODO: The following values are copied from cast module. We need to think about the
@@ -116,11 +118,13 @@
}
}
+ @Override
public synchronized void setCallback(@Nullable Callback callback) {
this.callback = callback;
}
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
+ @Override
public synchronized void startDiscovery() throws IOException {
if (multicastSocket != null) {
LOGGER.w("Discovery is already in progress.");
@@ -160,6 +164,7 @@
}
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
+ @Override
public void stopDiscovery() {
LOGGER.log("Stop discovery.");
if (multicastSocket == null && unicastSocket == null) {
@@ -195,11 +200,13 @@
}
/** Sends a mDNS request packet that asks for multicast response. */
+ @Override
public void sendMulticastPacket(@NonNull DatagramPacket packet) {
sendMdnsPacket(packet, multicastPacketQueue);
}
/** Sends a mDNS request packet that asks for unicast response. */
+ @Override
public void sendUnicastPacket(DatagramPacket packet) {
if (useSeparateSocketForUnicast) {
sendMdnsPacket(packet, unicastPacketQueue);
@@ -512,11 +519,4 @@
public boolean isOnIPv6OnlyNetwork() {
return multicastSocket != null && multicastSocket.isOnIPv6OnlyNetwork();
}
-
- /** Callback for {@link MdnsSocketClient}. */
- public interface Callback {
- void onResponseReceived(@NonNull MdnsResponse response);
-
- void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode);
- }
}
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClientBase.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
new file mode 100644
index 0000000..23504a0
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Network;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+
+/**
+ * Base class for multicast socket client.
+ *
+ * @hide
+ */
+public interface MdnsSocketClientBase {
+ /*** Start mDns discovery on given network. */
+ default void startDiscovery() throws IOException { }
+
+ /*** Stop mDns discovery. */
+ default void stopDiscovery() { }
+
+ /*** Set callback for receiving mDns response */
+ void setCallback(@Nullable Callback callback);
+
+ /*** Sends a mDNS request packet that asks for multicast response. */
+ void sendMulticastPacket(@NonNull DatagramPacket packet);
+
+ /**
+ * Sends a mDNS request packet via given network that asks for multicast response. Null network
+ * means sending packet via all networks.
+ */
+ default void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ throw new UnsupportedOperationException(
+ "This socket client doesn't support per-network sending");
+ }
+
+ /*** Sends a mDNS request packet that asks for unicast response. */
+ void sendUnicastPacket(@NonNull DatagramPacket packet);
+
+ /**
+ * Sends a mDNS request packet via given network that asks for unicast response. Null network
+ * means sending packet via all networks.
+ */
+ default void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ throw new UnsupportedOperationException(
+ "This socket client doesn't support per-network sending");
+ }
+
+ /*** Notify that the given network is requested for mdns discovery / resolution */
+ default void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
+ @Nullable Network network) { }
+
+ /*** Notify that the network is unrequested */
+ default void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) { }
+
+ /*** Callback for mdns response */
+ interface Callback {
+ /*** Receive a mdns response */
+ void onResponseReceived(@NonNull MdnsResponse response);
+
+ /*** Parse a mdns response failed */
+ void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode);
+ }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index d3bf060..9298852 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -35,6 +35,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.ip.NetlinkMonitor;
import com.android.net.module.util.netlink.NetlinkConstants;
@@ -42,7 +43,6 @@
import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException;
-import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
@@ -60,8 +60,13 @@
public class MdnsSocketProvider {
private static final String TAG = MdnsSocketProvider.class.getSimpleName();
private static final boolean DBG = MdnsDiscoveryManager.DBG;
+ // This buffer size matches what MdnsSocketClient uses currently.
+ // But 1440 should generally be enough because of standard Ethernet.
+ // Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
+ private static final int READ_BUFFER_SIZE = 2048;
private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
@NonNull private final Context mContext;
+ @NonNull private final Looper mLooper;
@NonNull private final Handler mHandler;
@NonNull private final Dependencies mDependencies;
@NonNull private final NetworkCallback mNetworkCallback;
@@ -75,6 +80,7 @@
new ArrayMap<>();
private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
private final List<String> mTetheredInterfaces = new ArrayList<>();
+ private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
private boolean mMonitoringSockets = false;
public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper) {
@@ -84,6 +90,7 @@
MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
@NonNull Dependencies deps) {
mContext = context;
+ mLooper = looper;
mHandler = new Handler(looper);
mDependencies = deps;
mNetworkCallback = new NetworkCallback() {
@@ -119,32 +126,33 @@
@VisibleForTesting
public static class Dependencies {
/*** Get network interface by given interface name */
- public NetworkInterfaceWrapper getNetworkInterfaceByName(String interfaceName)
+ public NetworkInterfaceWrapper getNetworkInterfaceByName(@NonNull String interfaceName)
throws SocketException {
final NetworkInterface ni = NetworkInterface.getByName(interfaceName);
return ni == null ? null : new NetworkInterfaceWrapper(ni);
}
/*** Check whether given network interface can support mdns */
- public boolean canScanOnInterface(NetworkInterfaceWrapper networkInterface) {
+ public boolean canScanOnInterface(@NonNull NetworkInterfaceWrapper networkInterface) {
return MulticastNetworkInterfaceProvider.canScanOnInterface(networkInterface);
}
/*** Create a MdnsInterfaceSocket */
- public MdnsInterfaceSocket createMdnsInterfaceSocket(NetworkInterface networkInterface,
- int port) throws IOException {
- return new MdnsInterfaceSocket(networkInterface, port);
+ public MdnsInterfaceSocket createMdnsInterfaceSocket(
+ @NonNull NetworkInterface networkInterface, int port, @NonNull Looper looper,
+ @NonNull byte[] packetReadBuffer) throws IOException {
+ return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
}
}
/*** Data class for storing socket related info */
private static class SocketInfo {
final MdnsInterfaceSocket mSocket;
- final List<LinkAddress> mAddresses = new ArrayList<>();
+ final List<LinkAddress> mAddresses;
SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses) {
mSocket = socket;
- mAddresses.addAll(addresses);
+ mAddresses = new ArrayList<>(addresses);
}
}
@@ -160,8 +168,9 @@
}
}
- private void ensureRunningOnHandlerThread() {
- if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ /*** Ensure that current running thread is same as given handler thread */
+ public static void ensureRunningOnHandlerThread(Handler handler) {
+ if (handler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException(
"Not running on Handler thread: " + Thread.currentThread().getName());
}
@@ -169,7 +178,7 @@
/*** Start monitoring sockets by listening callbacks for sockets creation or removal */
public void startMonitoringSockets() {
- ensureRunningOnHandlerThread();
+ ensureRunningOnHandlerThread(mHandler);
if (mMonitoringSockets) {
Log.d(TAG, "Already monitoring sockets.");
return;
@@ -188,7 +197,7 @@
/*** Stop monitoring sockets and unregister callbacks */
public void stopMonitoringSockets() {
- ensureRunningOnHandlerThread();
+ ensureRunningOnHandlerThread(mHandler);
if (!mMonitoringSockets) {
Log.d(TAG, "Monitoring sockets hasn't been started.");
return;
@@ -204,19 +213,15 @@
mMonitoringSockets = false;
}
- private static boolean isNetworkMatched(@Nullable Network targetNetwork,
+ /*** Check whether the target network is matched current network */
+ public static boolean isNetworkMatched(@Nullable Network targetNetwork,
@NonNull Network currentNetwork) {
return targetNetwork == null || targetNetwork.equals(currentNetwork);
}
private boolean matchRequestedNetwork(Network network) {
- for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
- final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
- if (isNetworkMatched(requestedNetwork, network)) {
- return true;
- }
- }
- return false;
+ return hasAllNetworksRequest()
+ || mCallbacksToRequestedNetworks.containsValue(network);
}
private boolean hasAllNetworksRequest() {
@@ -279,15 +284,6 @@
current.addAll(updated);
}
- private static List<LinkAddress> getLinkAddressFromNetworkInterface(
- NetworkInterfaceWrapper networkInterface) {
- List<LinkAddress> addresses = new ArrayList<>();
- for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
- addresses.add(new LinkAddress(address));
- }
- return addresses;
- }
-
private void createSocket(Network network, LinkProperties lp) {
final String interfaceName = lp.getInterfaceName();
if (interfaceName == null) {
@@ -307,10 +303,12 @@
+ " with interfaceName:" + interfaceName);
}
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
- networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT);
+ networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
+ mPacketReadBuffer);
final List<LinkAddress> addresses;
if (network.netId == INetd.LOCAL_NET_ID) {
- addresses = getLinkAddressFromNetworkInterface(networkInterface);
+ addresses = CollectionUtils.map(
+ networkInterface.getInterfaceAddresses(), LinkAddress::new);
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
} else {
addresses = lp.getLinkAddresses();
@@ -402,7 +400,7 @@
* @param cb the callback to listen the socket creation.
*/
public void requestSocket(@Nullable Network network, @NonNull SocketCallback cb) {
- ensureRunningOnHandlerThread();
+ ensureRunningOnHandlerThread(mHandler);
mCallbacksToRequestedNetworks.put(cb, network);
if (network == null) {
// Does not specify a required network, create sockets for all possible
@@ -425,7 +423,7 @@
/*** Unrequest the socket */
public void unrequestSocket(@NonNull SocketCallback cb) {
- ensureRunningOnHandlerThread();
+ ensureRunningOnHandlerThread(mHandler);
mCallbacksToRequestedNetworks.remove(cb);
if (hasAllNetworksRequest()) {
// Still has a request for all networks (interfaces).
@@ -434,16 +432,24 @@
// Check if remaining requests are matched any of sockets.
for (int i = mNetworkSockets.size() - 1; i >= 0; i--) {
- if (matchRequestedNetwork(mNetworkSockets.keyAt(i))) continue;
- mNetworkSockets.removeAt(i).mSocket.destroy();
+ final Network network = mNetworkSockets.keyAt(i);
+ if (matchRequestedNetwork(network)) continue;
+ final SocketInfo info = mNetworkSockets.removeAt(i);
+ info.mSocket.destroy();
+ // Still notify to unrequester for socket destroy.
+ cb.onInterfaceDestroyed(network, info.mSocket);
}
// Remove all sockets for tethering interface because these sockets do not have associated
// networks, and they should invoke by a request for all networks (interfaces). If there is
// no such request, the sockets for tethering interface should be removed.
for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
- mTetherInterfaceSockets.removeAt(i).mSocket.destroy();
+ final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
+ info.mSocket.destroy();
+ // Still notify to unrequester for socket destroy.
+ cb.onInterfaceDestroyed(new Network(INetd.LOCAL_NET_ID), info.mSocket);
}
+ mTetherInterfaceSockets.clear();
}
/*** Callbacks for listening socket changes */
diff --git a/service/mdns/com/android/server/connectivity/mdns/MulticastPacketReader.java b/service/mdns/com/android/server/connectivity/mdns/MulticastPacketReader.java
new file mode 100644
index 0000000..20cc47f
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MulticastPacketReader.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.system.Os;
+import android.util.ArraySet;
+
+import com.android.net.module.util.FdEventsReader;
+
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.util.Set;
+
+/** Simple reader for mDNS packets. */
+public class MulticastPacketReader extends FdEventsReader<MulticastPacketReader.RecvBuffer> {
+ @NonNull
+ private final String mLogTag;
+ @NonNull
+ private final ParcelFileDescriptor mSocket;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final Set<PacketHandler> mPacketHandlers = new ArraySet<>();
+
+ interface PacketHandler {
+ void handlePacket(byte[] recvbuf, int length, InetSocketAddress src);
+ }
+
+ public static final class RecvBuffer {
+ final byte[] data;
+ final InetSocketAddress src;
+
+ private RecvBuffer(byte[] data, InetSocketAddress src) {
+ this.data = data;
+ this.src = src;
+ }
+ }
+
+ /**
+ * Create a new {@link MulticastPacketReader}.
+ * @param socket Socket to read from. This will *not* be closed when the reader terminates.
+ * @param buffer Buffer to read packets into. Will only be used from the handler thread.
+ */
+ protected MulticastPacketReader(@NonNull String interfaceTag,
+ @NonNull ParcelFileDescriptor socket, @NonNull Handler handler,
+ @NonNull byte[] buffer) {
+ super(handler, new RecvBuffer(buffer, new InetSocketAddress()));
+ mLogTag = MulticastPacketReader.class.getSimpleName() + "/" + interfaceTag;
+ mSocket = socket;
+ mHandler = handler;
+ }
+
+ @Override
+ protected int recvBufSize(@NonNull RecvBuffer buffer) {
+ return buffer.data.length;
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ // Keep a reference to the PFD as it would close the fd in its finalizer otherwise
+ return mSocket.getFileDescriptor();
+ }
+
+ @Override
+ protected void onStop() {
+ // Do nothing (do not close the FD)
+ }
+
+ @Override
+ protected int readPacket(@NonNull FileDescriptor fd, @NonNull RecvBuffer buffer)
+ throws Exception {
+ return Os.recvfrom(
+ fd, buffer.data, 0, buffer.data.length, 0 /* flags */, buffer.src);
+ }
+
+ @Override
+ protected void handlePacket(@NonNull RecvBuffer recvbuf, int length) {
+ for (PacketHandler handler : mPacketHandlers) {
+ handler.handlePacket(recvbuf.data, length, recvbuf.src);
+ }
+ }
+
+ /**
+ * Add a packet handler to deal with received packets. If the handler is already set,
+ * this is a no-op.
+ */
+ public void addPacketHandler(@NonNull PacketHandler handler) {
+ ensureRunningOnHandlerThread(mHandler);
+ mPacketHandlers.add(handler);
+ }
+}
+
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8107be3..a7e6a2e 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -242,6 +242,8 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import androidx.annotation.RequiresApi;
+
import com.android.connectivity.resources.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -262,6 +264,10 @@
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.TcUtils;
import com.android.net.module.util.netlink.InetDiagMessage;
+import com.android.networkstack.apishim.BroadcastOptionsShimImpl;
+import com.android.networkstack.apishim.ConstantsShim;
+import com.android.networkstack.apishim.common.BroadcastOptionsShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
import com.android.server.connectivity.ClatCoordinator;
@@ -372,6 +378,10 @@
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
+ // Delimiter used when creating the broadcast delivery group for sending
+ // CONNECTIVITY_ACTION broadcast.
+ private static final char DELIVERY_GROUP_KEY_DELIMITER = ';';
+
// The maximum value for the blocking validation result, in milliseconds.
public static final int MAX_VALIDATION_IGNORE_AFTER_ROAM_TIME_MS = 10000;
@@ -1411,6 +1421,16 @@
+ ", ingress=true, PRIO_POLICE, ETH_P_ALL) failure: ", e);
}
}
+
+ /**
+ * Wraps {@link BroadcastOptionsShimImpl#newInstance(BroadcastOptions)}
+ */
+ // TODO: when available in all active branches:
+ // @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ public BroadcastOptionsShim makeBroadcastOptionsShim(BroadcastOptions options) {
+ return BroadcastOptionsShimImpl.newInstance(options);
+ }
}
public ConnectivityService(Context context) {
@@ -3037,6 +3057,7 @@
ConnectivityManager.EXTRA_NETWORK_INFO);
final BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+ applyMostRecentPolicyForConnectivityAction(opts, ni);
options = opts.toBundle();
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
@@ -3048,6 +3069,32 @@
}
}
+ private void applyMostRecentPolicyForConnectivityAction(BroadcastOptions options,
+ NetworkInfo info) {
+ // Delivery group policy APIs are only available on U+.
+ if (!SdkLevel.isAtLeastU()) return;
+
+ final BroadcastOptionsShim optsShim = mDeps.makeBroadcastOptionsShim(options);
+ try {
+ // This allows us to discard older broadcasts still waiting to be delivered
+ // which have the same namespace and key.
+ optsShim.setDeliveryGroupPolicy(ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT);
+ optsShim.setDeliveryGroupMatchingKey(ConnectivityManager.CONNECTIVITY_ACTION,
+ createDeliveryGroupKeyForConnectivityAction(info));
+ } catch (UnsupportedApiLevelException e) {
+ Log.wtf(TAG, "Using unsupported API" + e);
+ }
+ }
+
+ @VisibleForTesting
+ static String createDeliveryGroupKeyForConnectivityAction(NetworkInfo info) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(info.getType()).append(DELIVERY_GROUP_KEY_DELIMITER);
+ sb.append(info.getSubtype()).append(DELIVERY_GROUP_KEY_DELIMITER);
+ sb.append(info.getExtraInfo());
+ return sb.toString();
+ }
+
/**
* Called by SystemServer through ConnectivityManager when the system is ready.
*/
diff --git a/tests/cts/net/src/android/net/cts/RateLimitTest.java b/tests/cts/net/src/android/net/cts/RateLimitTest.java
index 28cec1a..36b98fc 100644
--- a/tests/cts/net/src/android/net/cts/RateLimitTest.java
+++ b/tests/cts/net/src/android/net/cts/RateLimitTest.java
@@ -301,29 +301,32 @@
public void testIngressRateLimit_testLimit() throws Exception {
assumeKernelSupport();
+ // These tests are not very precise, especially on lower-end devices.
+ // Add 30% tolerance to reduce test flakiness. Burst size is constant at 128KiB.
+ final double toleranceFactor = 1.3;
+
// If this value is too low, this test might become flaky because of the burst value that
// allows to send at a higher data rate for a short period of time. The faster the data rate
// and the longer the test, the less this test will be affected.
final long dataLimitInBytesPerSecond = 2_000_000; // 2MB/s
long resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(1));
assertGreaterThan("Failed initial test with rate limit disabled", resultInBytesPerSecond,
- dataLimitInBytesPerSecond);
+ (long) (dataLimitInBytesPerSecond * toleranceFactor));
// enable rate limit and wait until the tc filter is installed before starting the test.
ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext,
dataLimitInBytesPerSecond);
waitForTcPoliceFilterInstalled(Duration.ofSeconds(1));
- resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(10));
- // Add 10% tolerance to reduce test flakiness. Burst size is constant at 128KiB.
+ resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(15));
assertLessThan("Failed test with rate limit enabled", resultInBytesPerSecond,
- (long) (dataLimitInBytesPerSecond * 1.1));
+ (long) (dataLimitInBytesPerSecond * toleranceFactor));
ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, -1);
resultInBytesPerSecond = runIngressDataRateMeasurement(Duration.ofSeconds(1));
assertGreaterThan("Failed test with rate limit disabled", resultInBytesPerSecond,
- dataLimitInBytesPerSecond);
+ (long) (dataLimitInBytesPerSecond * toleranceFactor));
}
@Test
diff --git a/tests/unit/java/android/net/IpSecTransformTest.java b/tests/unit/java/android/net/IpSecTransformTest.java
index ec59064..8bc1bbd 100644
--- a/tests/unit/java/android/net/IpSecTransformTest.java
+++ b/tests/unit/java/android/net/IpSecTransformTest.java
@@ -143,8 +143,9 @@
@Test
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
- public void testStartMigration() throws Exception {
- mIpSecManager.startMigration(buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
+ public void testStartTransformMigration() throws Exception {
+ mIpSecManager.startTunnelModeTransformMigration(
+ buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
verify(mMockIpSecService)
.migrateTransform(
anyInt(),
@@ -155,9 +156,10 @@
@Test
@DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
- public void testStartMigrationOnSdkBeforeU() throws Exception {
+ public void testStartTransformMigrationOnSdkBeforeU() throws Exception {
try {
- mIpSecManager.startMigration(buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
+ mIpSecManager.startTunnelModeTransformMigration(
+ buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
fail("Expect to fail since migration is not supported before U");
} catch (UnsupportedOperationException expected) {
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index ca6a14b..7d64c26 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -151,6 +151,7 @@
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_PROFILE;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_VPN;
+import static com.android.server.ConnectivityService.createDeliveryGroupKeyForConnectivityAction;
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
@@ -210,6 +211,7 @@
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
@@ -360,6 +362,8 @@
import com.android.net.module.util.NetworkMonitorUtils;
import com.android.networkstack.apishim.ConstantsShim;
import com.android.networkstack.apishim.NetworkAgentConfigShimImpl;
+import com.android.networkstack.apishim.common.BroadcastOptionsShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
@@ -574,6 +578,7 @@
@Mock BpfNetMaps mBpfNetMaps;
@Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
@Mock TetheringManager mTetheringManager;
+ @Mock BroadcastOptionsShim mBroadcastOptionsShim;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -821,6 +826,25 @@
// null should not pass the test
return null;
}
+
+ @Override
+ public void sendStickyBroadcast(Intent intent, Bundle options) {
+ // Verify that delivery group policy APIs were used on U.
+ if (SdkLevel.isAtLeastU() && CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO,
+ NetworkInfo.class);
+ try {
+ verify(mBroadcastOptionsShim).setDeliveryGroupPolicy(
+ eq(ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT));
+ verify(mBroadcastOptionsShim).setDeliveryGroupMatchingKey(
+ eq(CONNECTIVITY_ACTION),
+ eq(createDeliveryGroupKeyForConnectivityAction(ni)));
+ } catch (UnsupportedApiLevelException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ super.sendStickyBroadcast(intent, options);
+ }
}
// This was only added in the T SDK, but this test needs to build against the R+S SDKs, too.
@@ -2053,6 +2077,12 @@
assertNotEquals(-1L, (long) mActiveRateLimit.getOrDefault(iface, -1L));
mActiveRateLimit.put(iface, -1L);
}
+
+ @Override
+ public BroadcastOptionsShim makeBroadcastOptionsShim(BroadcastOptions options) {
+ reset(mBroadcastOptionsShim);
+ return mBroadcastOptionsShim;
+ }
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -17353,4 +17383,14 @@
waitForIdle();
verify(mMockNetd).interfaceSetMtu(eq(ifaceName2), eq(mtu));
}
+
+ @Test
+ public void testCreateDeliveryGroupKeyForConnectivityAction() throws Exception {
+ final NetworkInfo info = new NetworkInfo(0 /* type */, 2 /* subtype */,
+ "MOBILE" /* typeName */, "LTE" /* subtypeName */);
+ assertEquals("0;2;null", createDeliveryGroupKeyForConnectivityAction(info));
+
+ info.setExtraInfo("test_info");
+ assertEquals("0;2;test_info", createDeliveryGroupKeyForConnectivityAction(info));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
index 2051e0c..961f0f0 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
@@ -27,7 +27,6 @@
import java.net.DatagramPacket
import java.net.Inet6Address
import java.net.InetAddress
-import java.net.InetSocketAddress
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import org.junit.After
@@ -37,6 +36,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.any
import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify
@@ -46,9 +46,6 @@
private const val NEXT_ANNOUNCES_DELAY = 1L
private const val TEST_TIMEOUT_MS = 1000L
-private val destinationsSupplier = {
- listOf(InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)) }
-
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsAnnouncerTest {
@@ -59,6 +56,7 @@
@Before
fun setUp() {
+ doReturn(true).`when`(socket).hasJoinedIpv6()
thread.start()
}
@@ -70,7 +68,7 @@
private class TestAnnouncementInfo(
announcedRecords: List<MdnsRecord>,
additionalRecords: List<MdnsRecord>
- ) : AnnouncementInfo(announcedRecords, additionalRecords, destinationsSupplier) {
+ ) : AnnouncementInfo(announcedRecords, additionalRecords) {
override fun getDelayMs(nextIndex: Int) =
if (nextIndex < FIRST_ANNOUNCES_COUNT) {
FIRST_ANNOUNCES_DELAY
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
index 3e3c3bf..83e7696 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -46,7 +46,7 @@
private static final String SERVICE_TYPE_2 = "_test._tcp.local";
@Mock private ExecutorProvider executorProvider;
- @Mock private MdnsSocketClient socketClient;
+ @Mock private MdnsSocketClientBase socketClient;
@Mock private MdnsServiceTypeClient mockServiceTypeClientOne;
@Mock private MdnsServiceTypeClient mockServiceTypeClientTwo;
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
new file mode 100644
index 0000000..ad22305
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.connectivity.mdns
+
+import android.net.InetAddresses.parseNumericAddress
+import android.net.LinkAddress
+import android.net.nsd.NsdServiceInfo
+import android.os.Build
+import android.os.HandlerThread
+import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
+import com.android.server.connectivity.mdns.MdnsInterfaceAdvertiser.EXIT_ANNOUNCEMENT_DELAY_MS
+import com.android.server.connectivity.mdns.MdnsPacketRepeater.PacketRepeaterCallback
+import com.android.server.connectivity.mdns.MdnsProber.ProbingInfo
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.waitForIdle
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+
+private const val LOG_TAG = "testlogtag"
+private const val TIMEOUT_MS = 10_000L
+
+private val TEST_ADDRS = listOf(LinkAddress(parseNumericAddress("2001:db8::123"), 64))
+private val TEST_BUFFER = ByteArray(1300)
+
+private const val TEST_SERVICE_ID_1 = 42
+private val TEST_SERVICE_1 = NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService"
+ port = 12345
+}
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsInterfaceAdvertiserTest {
+ private val socket = mock(MdnsInterfaceSocket::class.java)
+ private val thread = HandlerThread(MdnsInterfaceAdvertiserTest::class.simpleName)
+ private val cb = mock(MdnsInterfaceAdvertiser.Callback::class.java)
+ private val deps = mock(MdnsInterfaceAdvertiser.Dependencies::class.java)
+ private val repository = mock(MdnsRecordRepository::class.java)
+ private val replySender = mock(MdnsReplySender::class.java)
+ private val announcer = mock(MdnsAnnouncer::class.java)
+ private val prober = mock(MdnsProber::class.java)
+ private val probeCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
+ as ArgumentCaptor<PacketRepeaterCallback<ProbingInfo>>
+ private val announceCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
+ as ArgumentCaptor<PacketRepeaterCallback<AnnouncementInfo>>
+
+ private val probeCb get() = probeCbCaptor.value
+ private val announceCb get() = announceCbCaptor.value
+
+ private val advertiser by lazy {
+ MdnsInterfaceAdvertiser(LOG_TAG, socket, TEST_ADDRS, thread.looper, TEST_BUFFER, cb, deps)
+ }
+
+ @Before
+ fun setUp() {
+ doReturn(repository).`when`(deps).makeRecordRepository(any())
+ doReturn(replySender).`when`(deps).makeReplySender(any(), any(), any())
+ doReturn(announcer).`when`(deps).makeMdnsAnnouncer(any(), any(), any(), any())
+ doReturn(prober).`when`(deps).makeMdnsProber(any(), any(), any(), any())
+
+ doReturn(-1).`when`(repository).addService(anyInt(), any())
+ thread.start()
+ advertiser.start()
+
+ verify(deps).makeMdnsProber(any(), any(), any(), probeCbCaptor.capture())
+ verify(deps).makeMdnsAnnouncer(any(), any(), any(), announceCbCaptor.capture())
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ @Test
+ fun testAddRemoveService() {
+ val testProbingInfo = mock(ProbingInfo::class.java)
+ doReturn(TEST_SERVICE_ID_1).`when`(testProbingInfo).serviceId
+ doReturn(testProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
+
+ advertiser.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ verify(repository).addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ verify(prober).startProbing(testProbingInfo)
+
+ // Simulate probing success: continues to announcing
+ val testAnnouncementInfo = mock(AnnouncementInfo::class.java)
+ doReturn(testAnnouncementInfo).`when`(repository).onProbingSucceeded(testProbingInfo)
+ probeCb.onFinished(testProbingInfo)
+
+ verify(announcer).startSending(TEST_SERVICE_ID_1, testAnnouncementInfo,
+ 0L /* initialDelayMs */)
+
+ thread.waitForIdle(TIMEOUT_MS)
+ verify(cb).onRegisterServiceSucceeded(advertiser, TEST_SERVICE_ID_1)
+
+ // Remove the service: expect exit announcements
+ val testExitInfo = mock(AnnouncementInfo::class.java)
+ doReturn(testExitInfo).`when`(repository).exitService(TEST_SERVICE_ID_1)
+ advertiser.removeService(TEST_SERVICE_ID_1)
+
+ verify(announcer).startSending(TEST_SERVICE_ID_1, testExitInfo, EXIT_ANNOUNCEMENT_DELAY_MS)
+
+ // TODO: after exit announcements are implemented, verify that announceCb.onFinished causes
+ // cb.onDestroyed to be called.
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
new file mode 100644
index 0000000..9d42a65
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import static com.android.server.connectivity.mdns.MdnsSocketProvider.SocketCallback;
+import static com.android.server.connectivity.mdns.MulticastPacketReader.PacketHandler;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.net.InetAddresses;
+import android.net.Network;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.net.module.util.HexDump;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.net.DatagramPacket;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.List;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+public class MdnsMultinetworkSocketClientTest {
+ private static final byte[] BUFFER = new byte[10];
+ private static final long DEFAULT_TIMEOUT = 2000L;
+ @Mock private Network mNetwork;
+ @Mock private MdnsSocketProvider mProvider;
+ @Mock private MdnsInterfaceSocket mSocket;
+ @Mock private MdnsServiceBrowserListener mListener;
+ @Mock private MdnsSocketClientBase.Callback mCallback;
+ private MdnsMultinetworkSocketClient mSocketClient;
+ private Handler mHandler;
+
+ @Before
+ public void setUp() throws SocketException {
+ MockitoAnnotations.initMocks(this);
+ final HandlerThread thread = new HandlerThread("MdnsMultinetworkSocketClientTest");
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ mSocketClient = new MdnsMultinetworkSocketClient(thread.getLooper(), mProvider);
+ mHandler.post(() -> mSocketClient.setCallback(mCallback));
+ }
+
+ private SocketCallback expectSocketCallback() {
+ final ArgumentCaptor<SocketCallback> callbackCaptor =
+ ArgumentCaptor.forClass(SocketCallback.class);
+ mHandler.post(() -> mSocketClient.notifyNetworkRequested(mListener, mNetwork));
+ verify(mProvider, timeout(DEFAULT_TIMEOUT))
+ .requestSocket(eq(mNetwork), callbackCaptor.capture());
+ return callbackCaptor.getValue();
+ }
+
+ private NetworkInterface createEmptyNetworkInterface() {
+ try {
+ Constructor<NetworkInterface> constructor =
+ NetworkInterface.class.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ return constructor.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testSendPacket() throws IOException {
+ final SocketCallback callback = expectSocketCallback();
+ final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
+ final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */);
+ doReturn(true).when(mSocket).hasJoinedIpv4();
+ doReturn(true).when(mSocket).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ // Notify socket created
+ callback.onSocketCreated(mNetwork, mSocket, List.of());
+
+ // Send packet to IPv4 with target network and verify sending has been called.
+ mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket).send(ipv4Packet);
+
+ // Send packet to IPv6 without target network and verify sending has been called.
+ mSocketClient.sendMulticastPacket(ipv6Packet);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket).send(ipv6Packet);
+ }
+
+ @Test
+ public void testReceivePacket() {
+ final SocketCallback callback = expectSocketCallback();
+ final byte[] data = HexDump.hexStringToByteArray(
+ // scapy.raw(scapy.dns_compress(
+ // scapy.DNS(rd=0, qr=1, aa=1, qd = None,
+ // an =
+ // scapy.DNSRR(type='PTR', rrname='_testtype._tcp.local',
+ // rdata='testservice._testtype._tcp.local', rclass='IN', ttl=4500) /
+ // scapy.DNSRRSRV(rrname='testservice._testtype._tcp.local', rclass=0x8001,
+ // port=31234, target='Android.local', ttl=120))
+ // )).hex().upper()
+ "000084000000000200000000095F7465737474797065045F746370056C6F63616C00000C0001000011"
+ + "94000E0B7465737473657276696365C00CC02C00218001000000780010000000007A0207"
+ + "416E64726F6964C01B");
+
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ // Notify socket created
+ callback.onSocketCreated(mNetwork, mSocket, List.of());
+
+ final ArgumentCaptor<PacketHandler> handlerCaptor =
+ ArgumentCaptor.forClass(PacketHandler.class);
+ verify(mSocket).addPacketHandler(handlerCaptor.capture());
+
+ // Send the data and verify the received records.
+ final PacketHandler handler = handlerCaptor.getValue();
+ handler.handlePacket(data, data.length, null /* src */);
+ final ArgumentCaptor<MdnsResponse> responseCaptor =
+ ArgumentCaptor.forClass(MdnsResponse.class);
+ verify(mCallback).onResponseReceived(responseCaptor.capture());
+ final MdnsResponse response = responseCaptor.getValue();
+ assertTrue(response.hasPointerRecords());
+ assertArrayEquals("_testtype._tcp.local".split("\\."),
+ response.getPointerRecords().get(0).getName());
+ assertTrue(response.hasServiceRecord());
+ assertEquals("testservice", response.getServiceRecord().getServiceInstanceName());
+ assertEquals("Android.local".split("\\."),
+ response.getServiceRecord().getServiceHost());
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
index a98a4b2..3caa97d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -25,7 +25,6 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import java.net.DatagramPacket
-import java.net.InetSocketAddress
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
@@ -37,15 +36,13 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.any
import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.timeout
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-private val destinationsSupplier = {
- listOf(InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT)) }
-
private const val TEST_TIMEOUT_MS = 10_000L
private const val SHORT_TIMEOUT_MS = 200L
@@ -64,6 +61,7 @@
@Before
fun setUp() {
+ doReturn(true).`when`(socket).hasJoinedIpv6()
thread.start()
}
@@ -73,7 +71,7 @@
}
private class TestProbeInfo(probeRecords: List<MdnsRecord>, private val delayMs: Long = 1L) :
- ProbingInfo(1 /* serviceId */, probeRecords, destinationsSupplier) {
+ ProbingInfo(1 /* serviceId */, probeRecords) {
// Just send the packets quickly. Timing-related tests for MdnsPacketRepeater are already
// done in MdnsAnnouncerTest.
override fun getDelayMs(nextIndex: Int) = delayMs
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
new file mode 100644
index 0000000..502a36a
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.net.InetAddresses.parseNumericAddress
+import android.net.nsd.NsdServiceInfo
+import android.os.Build
+import android.os.HandlerThread
+import com.android.server.connectivity.mdns.MdnsRecordRepository.Dependencies
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import java.net.NetworkInterface
+import java.util.Collections
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_SERVICE_ID_1 = 42
+private const val TEST_SERVICE_ID_2 = 43
+private const val TEST_PORT = 12345
+private val TEST_HOSTNAME = arrayOf("Android_000102030405060708090A0B0C0D0E0F", "local")
+private val TEST_ADDRESSES = arrayOf(
+ parseNumericAddress("192.0.2.111"),
+ parseNumericAddress("2001:db8::111"),
+ parseNumericAddress("2001:db8::222"))
+
+private val TEST_SERVICE_1 = NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService"
+ port = TEST_PORT
+}
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsRecordRepositoryTest {
+ private val thread = HandlerThread(MdnsRecordRepositoryTest::class.simpleName)
+ private val deps = object : Dependencies() {
+ override fun getHostname() = TEST_HOSTNAME
+ override fun getInterfaceInetAddresses(iface: NetworkInterface) =
+ Collections.enumeration(TEST_ADDRESSES.toList())
+ }
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ @Test
+ fun testAddServiceAndProbe() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ assertEquals(0, repository.servicesCount)
+ assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
+ assertEquals(1, repository.servicesCount)
+
+ val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
+ assertNotNull(probingInfo)
+ assertTrue(repository.isProbing(TEST_SERVICE_ID_1))
+
+ assertEquals(TEST_SERVICE_ID_1, probingInfo.serviceId)
+ val packet = probingInfo.getPacket(0)
+
+ assertEquals(MdnsConstants.FLAGS_QUERY, packet.flags)
+ assertEquals(0, packet.answers.size)
+ assertEquals(0, packet.additionalRecords.size)
+
+ assertEquals(1, packet.questions.size)
+ val expectedName = arrayOf("MyTestService", "_testservice", "_tcp", "local")
+ assertEquals(MdnsAnyRecord(expectedName, false /* unicast */), packet.questions[0])
+
+ assertEquals(1, packet.authorityRecords.size)
+ assertEquals(MdnsServiceRecord(expectedName,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 120_000L /* ttlMillis */,
+ 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_PORT, TEST_HOSTNAME), packet.authorityRecords[0])
+
+ assertContentEquals(intArrayOf(TEST_SERVICE_ID_1), repository.clearServices())
+ }
+
+ @Test
+ fun testAddAndConflicts() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ assertFailsWith(NameConflictException::class) {
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1)
+ }
+ }
+
+ @Test
+ fun testExitingServiceReAdded() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.exitService(TEST_SERVICE_ID_1)
+
+ assertEquals(TEST_SERVICE_ID_1, repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1))
+ assertEquals(1, repository.servicesCount)
+
+ repository.removeService(TEST_SERVICE_ID_2)
+ assertEquals(0, repository.servicesCount)
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 697116c..a45ca68 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -62,7 +62,7 @@
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.Inet6Address;
-import java.net.SocketAddress;
+import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -80,7 +80,10 @@
private static final int INTERFACE_INDEX = 999;
private static final String SERVICE_TYPE = "_googlecast._tcp.local";
private static final String[] SERVICE_TYPE_LABELS = TextUtils.split(SERVICE_TYPE, "\\.");
- private static final Network NETWORK = mock(Network.class);
+ private static final InetSocketAddress IPV4_ADDRESS = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
+ private static final InetSocketAddress IPV6_ADDRESS = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
@Mock
private MdnsServiceBrowserListener mockListenerOne;
@@ -89,13 +92,16 @@
@Mock
private MdnsPacketWriter mockPacketWriter;
@Mock
- private MdnsSocketClient mockSocketClient;
+ private MdnsMultinetworkSocketClient mockSocketClient;
+ @Mock
+ private Network mockNetwork;
@Captor
private ArgumentCaptor<MdnsServiceInfo> serviceInfoCaptor;
private final byte[] buf = new byte[10];
- private DatagramPacket[] expectedPackets;
+ private DatagramPacket[] expectedIPv4Packets;
+ private DatagramPacket[] expectedIPv6Packets;
private ScheduledFuture<?>[] expectedSendFutures;
private FakeExecutor currentThreadExecutor = new FakeExecutor();
@@ -106,30 +112,52 @@
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
- expectedPackets = new DatagramPacket[16];
+ expectedIPv4Packets = new DatagramPacket[16];
+ expectedIPv6Packets = new DatagramPacket[16];
expectedSendFutures = new ScheduledFuture<?>[16];
for (int i = 0; i < expectedSendFutures.length; ++i) {
- expectedPackets[i] = new DatagramPacket(buf, 0, 5);
+ expectedIPv4Packets[i] = new DatagramPacket(buf, 0 /* offset */, 5 /* length */,
+ MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
+ expectedIPv6Packets[i] = new DatagramPacket(buf, 0 /* offset */, 5 /* length */,
+ MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
expectedSendFutures[i] = Mockito.mock(ScheduledFuture.class);
}
- when(mockPacketWriter.getPacket(any(SocketAddress.class)))
- .thenReturn(expectedPackets[0])
- .thenReturn(expectedPackets[1])
- .thenReturn(expectedPackets[2])
- .thenReturn(expectedPackets[3])
- .thenReturn(expectedPackets[4])
- .thenReturn(expectedPackets[5])
- .thenReturn(expectedPackets[6])
- .thenReturn(expectedPackets[7])
- .thenReturn(expectedPackets[8])
- .thenReturn(expectedPackets[9])
- .thenReturn(expectedPackets[10])
- .thenReturn(expectedPackets[11])
- .thenReturn(expectedPackets[12])
- .thenReturn(expectedPackets[13])
- .thenReturn(expectedPackets[14])
- .thenReturn(expectedPackets[15]);
+ when(mockPacketWriter.getPacket(IPV4_ADDRESS))
+ .thenReturn(expectedIPv4Packets[0])
+ .thenReturn(expectedIPv4Packets[1])
+ .thenReturn(expectedIPv4Packets[2])
+ .thenReturn(expectedIPv4Packets[3])
+ .thenReturn(expectedIPv4Packets[4])
+ .thenReturn(expectedIPv4Packets[5])
+ .thenReturn(expectedIPv4Packets[6])
+ .thenReturn(expectedIPv4Packets[7])
+ .thenReturn(expectedIPv4Packets[8])
+ .thenReturn(expectedIPv4Packets[9])
+ .thenReturn(expectedIPv4Packets[10])
+ .thenReturn(expectedIPv4Packets[11])
+ .thenReturn(expectedIPv4Packets[12])
+ .thenReturn(expectedIPv4Packets[13])
+ .thenReturn(expectedIPv4Packets[14])
+ .thenReturn(expectedIPv4Packets[15]);
+
+ when(mockPacketWriter.getPacket(IPV6_ADDRESS))
+ .thenReturn(expectedIPv6Packets[0])
+ .thenReturn(expectedIPv6Packets[1])
+ .thenReturn(expectedIPv6Packets[2])
+ .thenReturn(expectedIPv6Packets[3])
+ .thenReturn(expectedIPv6Packets[4])
+ .thenReturn(expectedIPv6Packets[5])
+ .thenReturn(expectedIPv6Packets[6])
+ .thenReturn(expectedIPv6Packets[7])
+ .thenReturn(expectedIPv6Packets[8])
+ .thenReturn(expectedIPv6Packets[9])
+ .thenReturn(expectedIPv6Packets[10])
+ .thenReturn(expectedIPv6Packets[11])
+ .thenReturn(expectedIPv6Packets[12])
+ .thenReturn(expectedIPv6Packets[13])
+ .thenReturn(expectedIPv6Packets[14])
+ .thenReturn(expectedIPv6Packets[15]);
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor) {
@@ -282,8 +310,8 @@
//MdnsConfigsFlagsImpl.alwaysAskForUnicastResponseInEachBurst.override(true);
MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
- QueryTaskConfig config =
- new QueryTaskConfig(searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1);
+ QueryTaskConfig config = new QueryTaskConfig(
+ searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1, mockNetwork);
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -311,8 +339,8 @@
public void testQueryTaskConfig_askForUnicastInFirstQuery() {
MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
- QueryTaskConfig config =
- new QueryTaskConfig(searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1);
+ QueryTaskConfig config = new QueryTaskConfig(
+ searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1, mockNetwork);
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -409,7 +437,7 @@
MdnsResponse response = mock(MdnsResponse.class);
when(response.getServiceInstanceName()).thenReturn("service-instance-1");
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
- doReturn(NETWORK).when(response).getNetwork();
+ doReturn(mockNetwork).when(response).getNetwork();
when(response.isComplete()).thenReturn(false);
client.processResponse(response);
@@ -423,7 +451,7 @@
List.of() /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
@@ -443,7 +471,7 @@
/* subtype= */ "ABCDE",
Collections.emptyMap(),
/* interfaceIndex= */ 20,
- NETWORK);
+ mockNetwork);
client.processResponse(initialResponse);
// Process a second response with a different port and updated text attributes.
@@ -455,7 +483,7 @@
/* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"),
/* interfaceIndex= */ 20,
- NETWORK);
+ mockNetwork);
client.processResponse(secondResponse);
// Verify onServiceNameDiscovered was called once for the initial response.
@@ -469,7 +497,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
20 /* interfaceIndex */,
- NETWORK);
+ mockNetwork);
// Verify onServiceFound was called once for the initial response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -480,7 +508,7 @@
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertNull(initialServiceInfo.getAttributeByKey("key"));
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
- assertEquals(NETWORK, initialServiceInfo.getNetwork());
+ assertEquals(mockNetwork, initialServiceInfo.getNetwork());
// Verify onServiceUpdated was called once for the second response.
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -492,7 +520,7 @@
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
- assertEquals(NETWORK, updatedServiceInfo.getNetwork());
+ assertEquals(mockNetwork, updatedServiceInfo.getNetwork());
}
@Test
@@ -509,7 +537,7 @@
/* subtype= */ "ABCDE",
Collections.emptyMap(),
/* interfaceIndex= */ 20,
- NETWORK);
+ mockNetwork);
client.processResponse(initialResponse);
// Process a second response with a different port and updated text attributes.
@@ -521,7 +549,7 @@
/* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"),
/* interfaceIndex= */ 20,
- NETWORK);
+ mockNetwork);
client.processResponse(secondResponse);
System.out.println("secondResponses ip"
@@ -538,7 +566,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
20 /* interfaceIndex */,
- NETWORK);
+ mockNetwork);
// Verify onServiceFound was called once for the initial response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -549,7 +577,7 @@
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertNull(initialServiceInfo.getAttributeByKey("key"));
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
- assertEquals(NETWORK, initialServiceInfo.getNetwork());
+ assertEquals(mockNetwork, initialServiceInfo.getNetwork());
// Verify onServiceUpdated was called once for the second response.
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -561,7 +589,7 @@
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
- assertEquals(NETWORK, updatedServiceInfo.getNetwork());
+ assertEquals(mockNetwork, updatedServiceInfo.getNetwork());
}
private void verifyServiceRemovedNoCallback(MdnsServiceBrowserListener listener) {
@@ -599,12 +627,12 @@
/* subtype= */ "ABCDE",
Collections.emptyMap(),
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
client.processResponse(initialResponse);
MdnsResponse response = mock(MdnsResponse.class);
doReturn("goodbye-service").when(response).getServiceInstanceName();
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
- doReturn(NETWORK).when(response).getNetwork();
+ doReturn(mockNetwork).when(response).getNetwork();
doReturn(true).when(response).isGoodbye();
client.processResponse(response);
// Verify removed callback won't be called if the service is not existed.
@@ -615,9 +643,9 @@
doReturn(serviceName).when(response).getServiceInstanceName();
client.processResponse(response);
verifyServiceRemovedCallback(
- mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, NETWORK);
+ mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, mockNetwork);
verifyServiceRemovedCallback(
- mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, NETWORK);
+ mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, mockNetwork);
}
@Test
@@ -631,7 +659,7 @@
/* subtype= */ "ABCDE",
Collections.emptyMap(),
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
client.processResponse(initialResponse);
client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
@@ -647,7 +675,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
// Verify onServiceFound was called once for the existing response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -684,7 +712,7 @@
MdnsResponse initialResponse =
createMockResponse(
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
- Map.of(), INTERFACE_INDEX, NETWORK);
+ Map.of(), INTERFACE_INDEX, mockNetwork);
client.processResponse(initialResponse);
// Clear the scheduled runnable.
@@ -718,7 +746,7 @@
MdnsResponse initialResponse =
createMockResponse(
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
- Map.of(), INTERFACE_INDEX, NETWORK);
+ Map.of(), INTERFACE_INDEX, mockNetwork);
client.processResponse(initialResponse);
// Clear the scheduled runnable.
@@ -737,7 +765,7 @@
// Verify removed callback was called.
verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
- INTERFACE_INDEX, NETWORK);
+ INTERFACE_INDEX, mockNetwork);
}
@Test
@@ -758,7 +786,7 @@
MdnsResponse initialResponse =
createMockResponse(
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
- Map.of(), INTERFACE_INDEX, NETWORK);
+ Map.of(), INTERFACE_INDEX, mockNetwork);
client.processResponse(initialResponse);
// Clear the scheduled runnable.
@@ -792,7 +820,7 @@
MdnsResponse initialResponse =
createMockResponse(
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
- Map.of(), INTERFACE_INDEX, NETWORK);
+ Map.of(), INTERFACE_INDEX, mockNetwork);
client.processResponse(initialResponse);
// Clear the scheduled runnable.
@@ -804,7 +832,7 @@
// Verify removed callback was called.
verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
- INTERFACE_INDEX, NETWORK);
+ INTERFACE_INDEX, mockNetwork);
}
@Test
@@ -824,7 +852,7 @@
"ABCDE" /* subtype */,
Collections.emptyMap(),
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
client.processResponse(initialResponse);
// Process a second response which has ip address to make response become complete.
@@ -836,7 +864,7 @@
"ABCDE" /* subtype */,
Collections.emptyMap(),
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
client.processResponse(secondResponse);
// Process a third response with a different ip address, port and updated text attributes.
@@ -848,7 +876,7 @@
"ABCDE" /* subtype */,
Collections.singletonMap("key", "value"),
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
client.processResponse(thirdResponse);
// Process the last response which is goodbye message.
@@ -868,7 +896,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
// Verify onServiceFound was second called for the second response.
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -881,7 +909,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
// Verify onServiceUpdated was third called for the third response.
inOrder.verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -894,7 +922,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
// Verify onServiceRemoved was called for the last response.
inOrder.verify(mockListenerOne).onServiceRemoved(serviceInfoCaptor.capture());
@@ -907,7 +935,7 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
// Verify onServiceNameRemoved was called for the last response.
inOrder.verify(mockListenerOne).onServiceNameRemoved(serviceInfoCaptor.capture());
@@ -920,18 +948,34 @@
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
INTERFACE_INDEX,
- NETWORK);
+ mockNetwork);
}
// verifies that the right query was enqueued with the right delay, and send query by executing
// the runnable.
private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse) {
+ verifyAndSendQuery(
+ index, timeInMs, expectsUnicastResponse, true /* multipleSocketDiscovery */);
+ }
+
+ private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse,
+ boolean multipleSocketDiscovery) {
assertEquals(currentThreadExecutor.getAndClearLastScheduledDelayInMs(), timeInMs);
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
if (expectsUnicastResponse) {
- verify(mockSocketClient).sendUnicastPacket(expectedPackets[index]);
+ verify(mockSocketClient).sendUnicastPacket(
+ expectedIPv4Packets[index], null /* network */);
+ if (multipleSocketDiscovery) {
+ verify(mockSocketClient).sendUnicastPacket(
+ expectedIPv6Packets[index], null /* network */);
+ }
} else {
- verify(mockSocketClient).sendMulticastPacket(expectedPackets[index]);
+ verify(mockSocketClient).sendMulticastPacket(
+ expectedIPv4Packets[index], null /* network */);
+ if (multipleSocketDiscovery) {
+ verify(mockSocketClient).sendMulticastPacket(
+ expectedIPv6Packets[index], null /* network */);
+ }
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index ef73030..07bbbb5 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -96,7 +96,7 @@
.getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
doReturn(mock(MdnsInterfaceSocket.class))
- .when(mDeps).createMdnsInterfaceSocket(any(), anyInt());
+ .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start();
mHandler = new Handler(thread.getLooper());
@@ -165,7 +165,7 @@
}
public void expectedSocketCreatedForNetwork(Network network, List<LinkAddress> addresses) {
- final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
+ final SocketEvent event = mHistory.poll(0L /* timeoutMs */, c -> true);
assertNotNull(event);
assertTrue(event instanceof SocketCreatedEvent);
assertEquals(network, event.mNetwork);
@@ -173,7 +173,7 @@
}
public void expectedInterfaceDestroyedForNetwork(Network network) {
- final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
+ final SocketEvent event = mHistory.poll(0L /* timeoutMs */, c -> true);
assertNotNull(event);
assertTrue(event instanceof InterfaceDestroyedEvent);
assertEquals(network, event.mNetwork);
@@ -181,7 +181,7 @@
public void expectedAddressesChangedForNetwork(Network network,
List<LinkAddress> addresses) {
- final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
+ final SocketEvent event = mHistory.poll(0L /* timeoutMs */, c -> true);
assertNotNull(event);
assertTrue(event instanceof AddressesChangedEvent);
assertEquals(network, event.mNetwork);
@@ -260,7 +260,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- testCallback3.expectedNoCallback();
+ // Expect the socket destroy for tethered interface.
+ testCallback3.expectedInterfaceDestroyedForNetwork(LOCAL_NETWORK);
}
@Test
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
index 804c2a4..2398349 100644
--- a/tools/gn2bp/Android.bp.swp
+++ b/tools/gn2bp/Android.bp.swp
@@ -31,37 +31,36 @@
name: "cronet_aml_api_sources",
srcs: [
":cronet_aml_components_cronet_android_interface_api_version",
- "components/cronet/android/api/src/org/chromium/net/BidirectionalStream.java",
- "components/cronet/android/api/src/org/chromium/net/CallbackException.java",
- "components/cronet/android/api/src/org/chromium/net/CronetEngine.java",
- "components/cronet/android/api/src/org/chromium/net/CronetException.java",
- "components/cronet/android/api/src/org/chromium/net/ExperimentalBidirectionalStream.java",
- "components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java",
- "components/cronet/android/api/src/org/chromium/net/ExperimentalUrlRequest.java",
- "components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java",
- "components/cronet/android/api/src/org/chromium/net/InlineExecutionProhibitedException.java",
- "components/cronet/android/api/src/org/chromium/net/NetworkException.java",
- "components/cronet/android/api/src/org/chromium/net/NetworkQualityRttListener.java",
- "components/cronet/android/api/src/org/chromium/net/NetworkQualityThroughputListener.java",
- "components/cronet/android/api/src/org/chromium/net/QuicException.java",
- "components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java",
- "components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java",
- "components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java",
- "components/cronet/android/api/src/org/chromium/net/UploadDataSink.java",
- "components/cronet/android/api/src/org/chromium/net/UrlRequest.java",
- "components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/ByteArrayCronetCallback.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetRequestCompletionListener.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetResponse.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/ImplicitFlowControlCallback.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/InMemoryTransformCronetCallback.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/JsonCronetCallback.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandler.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandlers.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/StringCronetCallback.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java",
- "components/cronet/android/api/src/org/chromium/net/apihelpers/UrlRequestCallbacks.java",
+ "components/cronet/android/api/src/android/net/http/BidirectionalStream.java",
+ "components/cronet/android/api/src/android/net/http/CallbackException.java",
+ "components/cronet/android/api/src/android/net/http/CronetEngine.java",
+ "components/cronet/android/api/src/android/net/http/CronetException.java",
+ "components/cronet/android/api/src/android/net/http/ExperimentalBidirectionalStream.java",
+ "components/cronet/android/api/src/android/net/http/ExperimentalCronetEngine.java",
+ "components/cronet/android/api/src/android/net/http/ExperimentalUrlRequest.java",
+ "components/cronet/android/api/src/android/net/http/ICronetEngineBuilder.java",
+ "components/cronet/android/api/src/android/net/http/InlineExecutionProhibitedException.java",
+ "components/cronet/android/api/src/android/net/http/NetworkException.java",
+ "components/cronet/android/api/src/android/net/http/NetworkQualityRttListener.java",
+ "components/cronet/android/api/src/android/net/http/NetworkQualityThroughputListener.java",
+ "components/cronet/android/api/src/android/net/http/QuicException.java",
+ "components/cronet/android/api/src/android/net/http/RequestFinishedInfo.java",
+ "components/cronet/android/api/src/android/net/http/UploadDataProvider.java",
+ "components/cronet/android/api/src/android/net/http/UploadDataSink.java",
+ "components/cronet/android/api/src/android/net/http/UrlRequest.java",
+ "components/cronet/android/api/src/android/net/http/UrlResponseInfo.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/ByteArrayCronetCallback.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/ContentTypeParametersParser.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/CronetRequestCompletionListener.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/CronetResponse.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/ImplicitFlowControlCallback.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/InMemoryTransformCronetCallback.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/JsonCronetCallback.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/RedirectHandler.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/RedirectHandlers.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/StringCronetCallback.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/UploadDataProviders.java",
+ "components/cronet/android/api/src/android/net/http/apihelpers/UrlRequestCallbacks.java",
],
}
@@ -1388,7 +1387,7 @@
cc_genrule {
name: "cronet_aml_base_build_date",
cmd: "$(location build/write_build_date_header.py) $(out) " +
- "1670130000",
+ "1672549200",
out: [
"base/generated_build_date.h",
],
@@ -1534,7 +1533,7 @@
// GN: //base:ios_cronet_buildflags
cc_genrule {
name: "cronet_aml_base_ios_cronet_buildflags",
- cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+ cmd: "echo '--flags CRONET_BUILD=\"true\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -2365,8 +2364,6 @@
":cronet_aml_components_cronet_android_cronet_static",
":cronet_aml_components_cronet_cronet_common",
":cronet_aml_components_cronet_metrics_util",
- ":cronet_aml_components_cronet_native_cronet_native_impl",
- ":cronet_aml_components_grpc_support_grpc_support",
":cronet_aml_components_metrics_library_support",
"components/cronet/android/cronet_jni.cc",
],
@@ -2563,7 +2560,6 @@
"base/android/java/src/org/chromium/base/Function.java",
"base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
"base/android/java/src/org/chromium/base/IntStringCallback.java",
- "base/android/java/src/org/chromium/base/IntentUtils.java",
"base/android/java/src/org/chromium/base/JNIUtils.java",
"base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
"base/android/java/src/org/chromium/base/JavaHandlerThread.java",
@@ -2657,19 +2653,6 @@
"base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
- "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
- "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
- "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
"base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
"base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
"base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
@@ -2719,8 +2702,6 @@
"components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
"components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
@@ -2832,7 +2813,6 @@
"base/android/java/src/org/chromium/base/Function.java",
"base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
"base/android/java/src/org/chromium/base/IntStringCallback.java",
- "base/android/java/src/org/chromium/base/IntentUtils.java",
"base/android/java/src/org/chromium/base/JNIUtils.java",
"base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
"base/android/java/src/org/chromium/base/JavaHandlerThread.java",
@@ -2926,19 +2906,6 @@
"base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
- "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
- "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
- "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
"base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
"base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
"base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
@@ -2988,8 +2955,6 @@
"components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
"components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
@@ -3144,9 +3109,6 @@
"buildtools/third_party/libc++/",
"buildtools/third_party/libc++/trunk/include",
"buildtools/third_party/libc++abi/trunk/include",
- "components/cronet/native/generated/",
- "components/cronet/native/include/",
- "components/grpc_support/include/",
"net/third_party/quiche/overrides/",
"net/third_party/quiche/src/",
"net/third_party/quiche/src/quiche/common/platform/default/",
@@ -3265,7 +3227,7 @@
"'API_LEVEL=19' " +
"-o " +
"$(out) " +
- "$(location components/cronet/android/api/src/org/chromium/net/ApiVersion.template)",
+ "$(location components/cronet/android/api/src/android/net/http/ApiVersion.template)",
out: [
"components/cronet/android/templates/org/chromium/net/ApiVersion.java",
],
@@ -3274,7 +3236,7 @@
"build/util/android_chrome_version.py",
"build/util/version.py",
"chrome/VERSION",
- "components/cronet/android/api/src/org/chromium/net/ApiVersion.template",
+ "components/cronet/android/api/src/android/net/http/ApiVersion.template",
],
}
@@ -3619,186 +3581,6 @@
},
}
-// GN: //components/cronet/native:cronet_native_impl
-cc_object {
- name: "cronet_aml_components_cronet_native_cronet_native_impl",
- srcs: [
- "components/cronet/native/buffer.cc",
- "components/cronet/native/engine.cc",
- "components/cronet/native/generated/cronet.idl_impl_interface.cc",
- "components/cronet/native/generated/cronet.idl_impl_struct.cc",
- "components/cronet/native/io_buffer_with_cronet_buffer.cc",
- "components/cronet/native/native_metrics_util.cc",
- "components/cronet/native/runnables.cc",
- "components/cronet/native/upload_data_sink.cc",
- "components/cronet/native/url_request.cc",
- ],
- shared_libs: [
- "libandroid",
- "liblog",
- "libz",
- ],
- static_libs: [
- "cronet_aml_base_allocator_partition_allocator_partition_alloc",
- "cronet_aml_base_base",
- "cronet_aml_base_base_static",
- "cronet_aml_base_third_party_double_conversion_double_conversion",
- "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
- "cronet_aml_components_prefs_prefs",
- "cronet_aml_crypto_crypto",
- "cronet_aml_net_net",
- "cronet_aml_net_preload_decoder",
- "cronet_aml_net_third_party_quiche_quiche",
- "cronet_aml_net_uri_template",
- "cronet_aml_third_party_boringssl_boringssl",
- "cronet_aml_third_party_brotli_common",
- "cronet_aml_third_party_brotli_dec",
- "cronet_aml_third_party_icu_icui18n",
- "cronet_aml_third_party_icu_icuuc_private",
- "cronet_aml_third_party_libevent_libevent",
- "cronet_aml_third_party_modp_b64_modp_b64",
- "cronet_aml_third_party_protobuf_protobuf_lite",
- "cronet_aml_url_url",
- ],
- generated_headers: [
- "cronet_aml_components_cronet_cronet_buildflags",
- "cronet_aml_components_cronet_cronet_version_header_action",
- "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
- ],
- defaults: [
- "cronet_aml_defaults",
- ],
- cflags: [
- "-DANDROID",
- "-DANDROID_NDK_VERSION_ROLL=r23_1",
- "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
- "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
- "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
- "-DGOOGLE_PROTOBUF_NO_RTTI",
- "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
- "-DHAVE_PTHREAD",
- "-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
- "-D_GNU_SOURCE",
- "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
- "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
- "-D__STDC_CONSTANT_MACROS",
- "-D__STDC_FORMAT_MACROS",
- ],
- local_include_dirs: [
- "./",
- "buildtools/third_party/libc++/",
- "buildtools/third_party/libc++/trunk/include",
- "buildtools/third_party/libc++abi/trunk/include",
- "components/cronet/native/generated/",
- "components/cronet/native/include/",
- "components/grpc_support/include/",
- "net/third_party/quiche/overrides/",
- "net/third_party/quiche/src/",
- "net/third_party/quiche/src/quiche/common/platform/default/",
- "third_party/abseil-cpp/",
- "third_party/boringssl/src/include/",
- "third_party/protobuf/src/",
- ],
- cpp_std: "c++17",
- target: {
- android_x86: {
- cflags: [
- "-msse3",
- ],
- },
- android_x86_64: {
- cflags: [
- "-msse3",
- ],
- },
- },
-}
-
-// GN: //components/grpc_support:grpc_support
-cc_object {
- name: "cronet_aml_components_grpc_support_grpc_support",
- srcs: [
- "components/grpc_support/bidirectional_stream.cc",
- "components/grpc_support/bidirectional_stream_c.cc",
- ],
- shared_libs: [
- "libandroid",
- "liblog",
- "libz",
- ],
- static_libs: [
- "cronet_aml_base_allocator_partition_allocator_partition_alloc",
- "cronet_aml_base_base",
- "cronet_aml_base_base_static",
- "cronet_aml_base_third_party_double_conversion_double_conversion",
- "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
- "cronet_aml_crypto_crypto",
- "cronet_aml_net_net",
- "cronet_aml_net_preload_decoder",
- "cronet_aml_net_third_party_quiche_quiche",
- "cronet_aml_net_uri_template",
- "cronet_aml_third_party_boringssl_boringssl",
- "cronet_aml_third_party_brotli_common",
- "cronet_aml_third_party_brotli_dec",
- "cronet_aml_third_party_icu_icui18n",
- "cronet_aml_third_party_icu_icuuc_private",
- "cronet_aml_third_party_libevent_libevent",
- "cronet_aml_third_party_modp_b64_modp_b64",
- "cronet_aml_third_party_protobuf_protobuf_lite",
- "cronet_aml_url_url",
- ],
- defaults: [
- "cronet_aml_defaults",
- ],
- cflags: [
- "-DANDROID",
- "-DANDROID_NDK_VERSION_ROLL=r23_1",
- "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
- "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
- "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
- "-DGOOGLE_PROTOBUF_NO_RTTI",
- "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
- "-DHAVE_PTHREAD",
- "-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
- "-D_GNU_SOURCE",
- "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
- "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
- "-D__STDC_CONSTANT_MACROS",
- "-D__STDC_FORMAT_MACROS",
- ],
- local_include_dirs: [
- "./",
- "buildtools/third_party/libc++/",
- "buildtools/third_party/libc++/trunk/include",
- "buildtools/third_party/libc++abi/trunk/include",
- "net/third_party/quiche/overrides/",
- "net/third_party/quiche/src/",
- "net/third_party/quiche/src/quiche/common/platform/default/",
- "third_party/abseil-cpp/",
- "third_party/boringssl/src/include/",
- "third_party/protobuf/src/",
- ],
- cpp_std: "c++17",
- target: {
- android_x86: {
- cflags: [
- "-msse3",
- ],
- },
- android_x86_64: {
- cflags: [
- "-msse3",
- ],
- },
- },
-}
-
// GN: //components/metrics:library_support
cc_object {
name: "cronet_aml_components_metrics_library_support",
@@ -4207,7 +3989,6 @@
"base/android/java/src/org/chromium/base/Function.java",
"base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
"base/android/java/src/org/chromium/base/IntStringCallback.java",
- "base/android/java/src/org/chromium/base/IntentUtils.java",
"base/android/java/src/org/chromium/base/JNIUtils.java",
"base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
"base/android/java/src/org/chromium/base/JavaHandlerThread.java",
@@ -4301,21 +4082,6 @@
"base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
"base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
- "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
- "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
- "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
- "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
- "base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl",
- "base/android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl",
"base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
"base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
"base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
@@ -4365,8 +4131,6 @@
"components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
"components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
- "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
"components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
"components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
@@ -4418,11 +4182,8 @@
"com.android.tethering",
],
libs: [
- "android-support-multidex",
"androidx.annotation_annotation",
"androidx.annotation_annotation-experimental-nodeps",
- "androidx.collection_collection",
- "androidx.core_core-nodeps",
"cronet_aml_api_java",
"framework-connectivity-t.stubs.module_lib",
"framework-connectivity.stubs.module_lib",
@@ -5010,7 +4771,7 @@
// GN: //net:ios_cronet_buildflags
cc_genrule {
name: "cronet_aml_net_ios_cronet_buildflags",
- cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+ cmd: "echo '--flags CRONET_BUILD=\"true\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
diff --git a/tools/gn2bp/desc_arm.json b/tools/gn2bp/desc_arm.json
index dcfcd63..b59157a 100644
--- a/tools/gn2bp/desc_arm.json
+++ b/tools/gn2bp/desc_arm.json
Binary files differ
diff --git a/tools/gn2bp/desc_arm64.json b/tools/gn2bp/desc_arm64.json
index 6a26578..9881124 100644
--- a/tools/gn2bp/desc_arm64.json
+++ b/tools/gn2bp/desc_arm64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x64.json b/tools/gn2bp/desc_x64.json
index f69f50f..3a2ce2a 100644
--- a/tools/gn2bp/desc_x64.json
+++ b/tools/gn2bp/desc_x64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x86.json b/tools/gn2bp/desc_x86.json
index 4f30229..03bcf2f 100644
--- a/tools/gn2bp/desc_x86.json
+++ b/tools/gn2bp/desc_x86.json
Binary files differ
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index 27c3b11..dcf8897 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -1516,10 +1516,7 @@
module.libs = {
"androidx.annotation_annotation",
"jsr305",
- "androidx.core_core-nodeps",
- "androidx.collection_collection",
"androidx.annotation_annotation-experimental-nodeps",
- "android-support-multidex",
"framework-connectivity.stubs.module_lib",
"framework-connectivity-t.stubs.module_lib",
"framework-tethering.stubs.module_lib",
diff --git a/tools/gn2bp/gen_desc_json.sh b/tools/gn2bp/gen_desc_json.sh
index 510b967..ed684b3 100755
--- a/tools/gn2bp/gen_desc_json.sh
+++ b/tools/gn2bp/gen_desc_json.sh
@@ -30,6 +30,7 @@
"use_hashed_jni_names = true"
"treat_warnings_as_errors = false"
"enable_base_tracing = false"
+ "is_cronet_build = true"
)
gn_args+=("target_cpu = \"${1}\"")