Use MDns aidl on NsdService
- Use MDns aidl to communicate with mdns service and register
event listener to receive callback.
- Remove all NDC relevant code on NsdService.
- Use MDns aidl on NsdServiceTest.
Bug: 209894875
Test: atest FrameworksNetTests CtsNetTestCases
Change-Id: I65929dee3838fef753396e86c665abd66b6fec81
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 209f372..33b44c8 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -235,7 +235,7 @@
public static final int DISABLE = 21;
/** @hide */
- public static final int NATIVE_DAEMON_EVENT = 22;
+ public static final int MDNS_SERVICE_EVENT = 22;
/** @hide */
public static final int REGISTER_CLIENT = 23;
@@ -268,7 +268,7 @@
EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP");
EVENT_NAMES.put(ENABLE, "ENABLE");
EVENT_NAMES.put(DISABLE, "DISABLE");
- EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT");
+ EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT");
}
/** @hide */
diff --git a/framework-t/src/android/net/nsd/NsdServiceInfo.java b/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 8506db1..2621594 100644
--- a/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -24,7 +24,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Base64;
import android.util.Log;
import java.io.UnsupportedEncodingException;
@@ -106,13 +105,11 @@
/**
* Unpack txt information from a base-64 encoded byte array.
*
- * @param rawRecords The raw base64 encoded records string read from netd.
+ * @param txtRecordsRawBytes The raw base64 encoded byte array.
*
* @hide
*/
- public void setTxtRecords(@NonNull String rawRecords) {
- byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT);
-
+ public void setTxtRecords(@NonNull byte[] txtRecordsRawBytes) {
// There can be multiple TXT records after each other. Each record has to following format:
//
// byte type required meaning
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index ddf6d2c..995f8ae 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -16,15 +16,24 @@
package com.android.server;
+import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.mdns.aidl.DiscoveryInfo;
+import android.net.mdns.aidl.GetAddressInfo;
+import android.net.mdns.aidl.IMDnsEventListener;
+import android.net.mdns.aidl.RegistrationInfo;
+import android.net.mdns.aidl.ResolutionInfo;
import android.net.nsd.INsdManager;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
+import android.net.nsd.MDnsManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
@@ -33,7 +42,6 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -42,7 +50,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.net.module.util.DnsSdTxtRecord;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,9 +57,7 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
-import java.util.Arrays;
import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
/**
* Network Service Discovery Service handles remote service discovery operation requests by
@@ -64,14 +69,18 @@
private static final String TAG = "NsdService";
private static final String MDNS_TAG = "mDnsConnector";
- private static final boolean DBG = true;
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
private final Context mContext;
private final NsdStateMachine mNsdStateMachine;
- private final DaemonConnection mDaemon;
- private final NativeCallbackReceiver mDaemonCallback;
+ private final MDnsManager mMDnsManager;
+ private final MDnsEventCallback mMDnsEventCallback;
+ // WARNING : Accessing this value in any thread is not safe, it must only be changed in the
+ // state machine thread. If change this outside state machine, it will need to introduce
+ // synchronization.
+ private boolean mIsDaemonStarted = false;
/**
* Clients receiving asynchronous messages
@@ -100,10 +109,26 @@
}
private void maybeStartDaemon() {
- mDaemon.maybeStart();
+ if (mIsDaemonStarted) {
+ if (DBG) Log.d(TAG, "Daemon is already started.");
+ return;
+ }
+ mMDnsManager.registerEventListener(mMDnsEventCallback);
+ mMDnsManager.startDaemon();
+ mIsDaemonStarted = true;
maybeScheduleStop();
}
+ private void maybeStopDaemon() {
+ if (!mIsDaemonStarted) {
+ if (DBG) Log.d(TAG, "Daemon has not been started.");
+ return;
+ }
+ mMDnsManager.unregisterEventListener(mMDnsEventCallback);
+ mMDnsManager.stopDaemon();
+ mIsDaemonStarted = false;
+ }
+
private boolean isAnyRequestActive() {
return mIdToClientInfoMap.size() != 0;
}
@@ -198,7 +223,7 @@
}
break;
case NsdManager.DAEMON_CLEANUP:
- mDaemon.maybeStop();
+ maybeStopDaemon();
break;
// This event should be only sent by the legacy (target SDK < S) clients.
// Mark the sending client as legacy.
@@ -211,7 +236,6 @@
maybeStartDaemon();
}
break;
- case NsdManager.NATIVE_DAEMON_EVENT:
default:
Log.e(TAG, "Unhandled " + msg);
return NOT_HANDLED;
@@ -397,9 +421,8 @@
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
- case NsdManager.NATIVE_DAEMON_EVENT:
- NativeEvent event = (NativeEvent) msg.obj;
- if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
+ case MDNS_SERVICE_EVENT:
+ if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
return NOT_HANDLED;
}
break;
@@ -409,13 +432,11 @@
return HANDLED;
}
- private boolean handleNativeEvent(int code, String raw, String[] cooked) {
+ private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
NsdServiceInfo servInfo;
- int id = Integer.parseInt(cooked[1]);
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
if (clientInfo == null) {
- String name = NativeResponseCode.nameOf(code);
- Log.e(TAG, String.format("id %d for %s has no client mapping", id, name));
+ Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
return false;
}
@@ -425,27 +446,20 @@
// This can happen because of race conditions. For example,
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
// and we may get in this situation.
- String name = NativeResponseCode.nameOf(code);
- Log.d(TAG, String.format(
- "Notification %s for listener id %d that is no longer active",
- name, id));
+ Log.d(TAG, String.format("%d for listener id %d that is no longer active",
+ code, id));
return false;
}
if (DBG) {
- String name = NativeResponseCode.nameOf(code);
- Log.d(TAG, String.format("Native daemon message %s: %s", name, raw));
+ Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
}
switch (code) {
- case NativeResponseCode.SERVICE_FOUND:
- /* NNN uniqueId serviceName regType domain interfaceIdx netId */
- servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- final int foundNetId;
- try {
- foundNetId = Integer.parseInt(cooked[6]);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
- break;
- }
+ case IMDnsEventListener.SERVICE_FOUND: {
+ final DiscoveryInfo info = (DiscoveryInfo) obj;
+ final String name = info.serviceName;
+ final String type = info.registrationType;
+ servInfo = new NsdServiceInfo(name, type);
+ final int foundNetId = info.netId;
if (foundNetId == 0L) {
// Ignore services that do not have a Network: they are not usable
// by apps, as they would need privileged permissions to use
@@ -455,74 +469,65 @@
servInfo.setNetwork(new Network(foundNetId));
clientInfo.onServiceFound(clientId, servInfo);
break;
- case NativeResponseCode.SERVICE_LOST:
- /* NNN uniqueId serviceName regType domain interfaceIdx netId */
- final int lostNetId;
- try {
- lostNetId = Integer.parseInt(cooked[6]);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
- break;
- }
- servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
+ }
+ case IMDnsEventListener.SERVICE_LOST: {
+ final DiscoveryInfo info = (DiscoveryInfo) obj;
+ final String name = info.serviceName;
+ final String type = info.registrationType;
+ final int lostNetId = info.netId;
+ servInfo = new NsdServiceInfo(name, type);
// The network could be null if it was torn down when the service is lost
// TODO: avoid returning null in that case, possibly by remembering found
// services on the same interface index and their network at the time
servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
clientInfo.onServiceLost(clientId, servInfo);
break;
- case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
- /* NNN uniqueId errorCode */
+ }
+ case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
clientInfo.onDiscoverServicesFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
- case NativeResponseCode.SERVICE_REGISTERED:
- /* NNN regId serviceName regType */
- servInfo = new NsdServiceInfo(cooked[2], null);
+ case IMDnsEventListener.SERVICE_REGISTERED: {
+ final RegistrationInfo info = (RegistrationInfo) obj;
+ final String name = info.serviceName;
+ servInfo = new NsdServiceInfo(name, null /* serviceType */);
clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
break;
- case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
- /* NNN regId errorCode */
+ }
+ case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
clientInfo.onRegisterServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
- case NativeResponseCode.SERVICE_UPDATED:
- /* NNN regId */
- break;
- case NativeResponseCode.SERVICE_UPDATE_FAILED:
- /* NNN regId errorCode */
- break;
- case NativeResponseCode.SERVICE_RESOLVED:
- /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */
+ case IMDnsEventListener.SERVICE_RESOLVED: {
+ final ResolutionInfo info = (ResolutionInfo) obj;
int index = 0;
- while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
- if (cooked[2].charAt(index) == '\\') {
+ final String fullName = info.serviceFullName;
+ while (index < fullName.length() && fullName.charAt(index) != '.') {
+ if (fullName.charAt(index) == '\\') {
++index;
}
++index;
}
- if (index >= cooked[2].length()) {
- Log.e(TAG, "Invalid service found " + raw);
+ if (index >= fullName.length()) {
+ Log.e(TAG, "Invalid service found " + fullName);
break;
}
- String name = cooked[2].substring(0, index);
- String rest = cooked[2].substring(index);
+ String name = fullName.substring(0, index);
+ String rest = fullName.substring(index);
String type = rest.replace(".local.", "");
- name = unescape(name);
-
clientInfo.mResolvedService.setServiceName(name);
clientInfo.mResolvedService.setServiceType(type);
- clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
- clientInfo.mResolvedService.setTxtRecords(cooked[6]);
+ clientInfo.mResolvedService.setPort(info.port);
+ clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
// Network will be added after SERVICE_GET_ADDR_SUCCESS
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
- int id2 = getUniqueId();
- if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) {
+ final int id2 = getUniqueId();
+ if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
clientInfo.onResolveServiceFailed(
@@ -530,7 +535,8 @@
clientInfo.mResolvedService = null;
}
break;
- case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
+ }
+ case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
/* NNN resolveId errorCode */
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
@@ -538,7 +544,7 @@
clientInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
- case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
+ case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
@@ -546,19 +552,15 @@
clientInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
- case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
+ case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
/* NNN resolveId hostname ttl addr interfaceIdx netId */
- Network network = null;
- try {
- final int netId = Integer.parseInt(cooked[6]);
- network = netId == 0L ? null : new Network(netId);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e);
- }
-
+ final GetAddressInfo info = (GetAddressInfo) obj;
+ final String address = info.address;
+ final int netId = info.netId;
+ final Network network = netId == NETID_UNSET ? null : new Network(netId);
InetAddress serviceHost = null;
try {
- serviceHost = InetAddress.getByName(cooked[4]);
+ serviceHost = InetAddress.getByName(address);
} catch (UnknownHostException e) {
Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
}
@@ -579,6 +581,7 @@
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
break;
+ }
default:
return false;
}
@@ -587,50 +590,66 @@
}
}
- private String unescape(String s) {
- StringBuilder sb = new StringBuilder(s.length());
- for (int i = 0; i < s.length(); ++i) {
- char c = s.charAt(i);
- if (c == '\\') {
- if (++i >= s.length()) {
- Log.e(TAG, "Unexpected end of escape sequence in: " + s);
- break;
- }
- c = s.charAt(i);
- if (c != '.' && c != '\\') {
- if (i + 2 >= s.length()) {
- Log.e(TAG, "Unexpected end of escape sequence in: " + s);
- break;
- }
- c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
- i += 2;
- }
- }
- sb.append(c);
- }
- return sb.toString();
- }
-
@VisibleForTesting
- NsdService(Context ctx, Handler handler, DaemonConnectionSupplier fn, long cleanupDelayMs) {
+ NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
mCleanupDelayMs = cleanupDelayMs;
mContext = ctx;
mNsdStateMachine = new NsdStateMachine(TAG, handler);
mNsdStateMachine.start();
- mDaemonCallback = new NativeCallbackReceiver();
- mDaemon = fn.get(mDaemonCallback);
+ mMDnsManager = ctx.getSystemService(MDnsManager.class);
+ mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
}
public static NsdService create(Context context) throws InterruptedException {
HandlerThread thread = new HandlerThread(TAG);
thread.start();
Handler handler = new Handler(thread.getLooper());
- NsdService service =
- new NsdService(context, handler, DaemonConnection::new, CLEANUP_DELAY_MS);
- service.mDaemonCallback.awaitConnection();
+ NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
return service;
}
+ private static class MDnsEventCallback extends IMDnsEventListener.Stub {
+ private final StateMachine mStateMachine;
+
+ MDnsEventCallback(StateMachine sm) {
+ mStateMachine = sm;
+ }
+
+ @Override
+ public void onServiceRegistrationStatus(final RegistrationInfo status) {
+ mStateMachine.sendMessage(
+ MDNS_SERVICE_EVENT, status.result, status.id, status);
+ }
+
+ @Override
+ public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
+ mStateMachine.sendMessage(
+ MDNS_SERVICE_EVENT, status.result, status.id, status);
+ }
+
+ @Override
+ public void onServiceResolutionStatus(final ResolutionInfo status) {
+ mStateMachine.sendMessage(
+ MDNS_SERVICE_EVENT, status.result, status.id, status);
+ }
+
+ @Override
+ public void onGettingServiceAddressStatus(final GetAddressInfo status) {
+ mStateMachine.sendMessage(
+ MDNS_SERVICE_EVENT, status.result, status.id, status);
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return this.HASH;
+ }
+ }
+
@Override
public INsdServiceConnector connect(INsdManagerCallback cb) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
@@ -711,140 +730,6 @@
return mUniqueId;
}
- /* These should be in sync with system/netd/server/ResponseCode.h */
- static final class NativeResponseCode {
- public static final int SERVICE_DISCOVERY_FAILED = 602;
- public static final int SERVICE_FOUND = 603;
- public static final int SERVICE_LOST = 604;
-
- public static final int SERVICE_REGISTRATION_FAILED = 605;
- public static final int SERVICE_REGISTERED = 606;
-
- public static final int SERVICE_RESOLUTION_FAILED = 607;
- public static final int SERVICE_RESOLVED = 608;
-
- public static final int SERVICE_UPDATED = 609;
- public static final int SERVICE_UPDATE_FAILED = 610;
-
- public static final int SERVICE_GET_ADDR_FAILED = 611;
- public static final int SERVICE_GET_ADDR_SUCCESS = 612;
-
- private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
- static {
- CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
- CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
- CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
- CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
- CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
- CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
- CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
- CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
- CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
- CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
- CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
- }
-
- static String nameOf(int code) {
- String name = CODE_NAMES.get(code);
- if (name == null) {
- return Integer.toString(code);
- }
- return name;
- }
- }
-
- private class NativeEvent {
- final int code;
- final String raw;
- final String[] cooked;
-
- NativeEvent(int code, String raw, String[] cooked) {
- this.code = code;
- this.raw = raw;
- this.cooked = cooked;
- }
- }
-
- class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
- private final CountDownLatch connected = new CountDownLatch(1);
-
- public void awaitConnection() throws InterruptedException {
- connected.await();
- }
-
- @Override
- public void onDaemonConnected() {
- connected.countDown();
- }
-
- @Override
- public boolean onCheckHoldWakeLock(int code) {
- return false;
- }
-
- @Override
- public boolean onEvent(int code, String raw, String[] cooked) {
- // TODO: NDC translates a message to a callback, we could enhance NDC to
- // directly interact with a state machine through messages
- NativeEvent event = new NativeEvent(code, raw, cooked);
- mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
- return true;
- }
- }
-
- interface DaemonConnectionSupplier {
- DaemonConnection get(NativeCallbackReceiver callback);
- }
-
- @VisibleForTesting
- public static class DaemonConnection {
- final NativeDaemonConnector mNativeConnector;
- boolean mIsStarted = false;
-
- DaemonConnection(NativeCallbackReceiver callback) {
- mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
- new Thread(mNativeConnector, MDNS_TAG).start();
- }
-
- /**
- * Executes the specified cmd on the daemon.
- */
- public boolean execute(Object... args) {
- if (DBG) {
- Log.d(TAG, "mdnssd " + Arrays.toString(args));
- }
- try {
- mNativeConnector.execute("mdnssd", args);
- } catch (NativeDaemonConnectorException e) {
- Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
- return false;
- }
- return true;
- }
-
- /**
- * Starts the daemon if it is not already started.
- */
- public void maybeStart() {
- if (mIsStarted) {
- return;
- }
- execute("start-service");
- mIsStarted = true;
- }
-
- /**
- * Stops the daemon if it is started.
- */
- public void maybeStop() {
- if (!mIsStarted) {
- return;
- }
- execute("stop-service");
- mIsStarted = false;
- }
- }
-
private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) {
Log.d(TAG, "registerService: " + regId + " " + service);
@@ -853,34 +738,26 @@
String type = service.getServiceType();
int port = service.getPort();
byte[] textRecord = service.getTxtRecord();
- String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
- return mDaemon.execute("register", regId, name, type, port, record);
+ return mMDnsManager.registerService(regId, name, type, port, textRecord, IFACE_IDX_ANY);
}
private boolean unregisterService(int regId) {
- return mDaemon.execute("stop-register", regId);
- }
-
- private boolean updateService(int regId, DnsSdTxtRecord t) {
- if (t == null) {
- return false;
- }
- return mDaemon.execute("update", regId, t.size(), t.getRawData());
+ return mMDnsManager.stopOperation(regId);
}
private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
final Network network = serviceInfo.getNetwork();
+ final String type = serviceInfo.getServiceType();
final int discoverInterface = getNetworkInterfaceIndex(network);
if (network != null && discoverInterface == IFACE_IDX_ANY) {
Log.e(TAG, "Interface to discover service on not found");
return false;
}
- return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(),
- discoverInterface);
+ return mMDnsManager.discover(discoveryId, type, discoverInterface);
}
private boolean stopServiceDiscovery(int discoveryId) {
- return mDaemon.execute("stop-discover", discoveryId);
+ return mMDnsManager.stopOperation(discoveryId);
}
private boolean resolveService(int resolveId, NsdServiceInfo service) {
@@ -892,7 +769,7 @@
Log.e(TAG, "Interface to resolve service on not found");
return false;
}
- return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface);
+ return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
}
/**
@@ -933,16 +810,15 @@
}
private boolean stopResolveService(int resolveId) {
- return mDaemon.execute("stop-resolve", resolveId);
+ return mMDnsManager.stopOperation(resolveId);
}
- private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) {
- // interfaceIdx is always obtained (as string) from the service resolved callback
- return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx);
+ private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
+ return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
}
private boolean stopGetAddrInfo(int resolveId) {
- return mDaemon.execute("stop-getaddrinfo", resolveId);
+ return mMDnsManager.stopOperation(resolveId);
}
@Override
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 5086943..3c228d0 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -19,10 +19,12 @@
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -36,6 +38,7 @@
import android.content.Context;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
+import android.net.nsd.MDnsManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Binder;
@@ -49,9 +52,6 @@
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
-import com.android.server.NsdService.DaemonConnection;
-import com.android.server.NsdService.DaemonConnectionSupplier;
-import com.android.server.NsdService.NativeCallbackReceiver;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -63,10 +63,8 @@
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.LinkedList;
import java.util.Queue;
@@ -92,8 +90,7 @@
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext;
@Mock ContentResolver mResolver;
- NativeCallbackReceiver mDaemonCallback;
- @Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
+ @Mock MDnsManager mMockMDnsM;
HandlerThread mThread;
TestHandler mHandler;
@@ -112,9 +109,17 @@
MockitoAnnotations.initMocks(this);
mThread = new HandlerThread("mock-service-handler");
mThread.start();
- doReturn(true).when(mDaemon).execute(any());
mHandler = new TestHandler(mThread.getLooper());
when(mContext.getContentResolver()).thenReturn(mResolver);
+ doReturn(MDnsManager.MDNS_SERVICE).when(mContext)
+ .getSystemServiceName(MDnsManager.class);
+ doReturn(mMockMDnsM).when(mContext).getSystemService(MDnsManager.MDNS_SERVICE);
+ doReturn(true).when(mMockMDnsM).registerService(
+ anyInt(), anyString(), anyString(), anyInt(), any(), anyInt());
+ doReturn(true).when(mMockMDnsM).stopOperation(anyInt());
+ doReturn(true).when(mMockMDnsM).discover(anyInt(), anyString(), anyInt());
+ doReturn(true).when(mMockMDnsM).resolve(
+ anyInt(), anyString(), anyString(), anyString(), anyInt());
}
@After
@@ -135,24 +140,25 @@
waitForIdle();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
- verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommands("start-service");
+ verify(mMockMDnsM, times(1)).registerEventListener(any());
+ verify(mMockMDnsM, times(1)).startDaemon();
connectClient(service);
waitForIdle();
final INsdManagerCallback cb2 = getCallback();
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
- verify(mDaemon, times(1)).maybeStart();
+ // Daemon has been started, it should not try to start it again.
+ verify(mMockMDnsM, times(1)).registerEventListener(any());
+ verify(mMockMDnsM, times(1)).startDaemon();
deathRecipient1.binderDied();
// Still 1 client remains, daemon shouldn't be stopped.
waitForIdle();
- verify(mDaemon, never()).maybeStop();
+ verify(mMockMDnsM, never()).stopDaemon();
deathRecipient2.binderDied();
// All clients are disconnected, the daemon should be stopped.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
- verifyDaemonCommands("stop-service");
}
@Test
@@ -160,28 +166,30 @@
public void testNoDaemonStartedWhenClientsConnect() throws Exception {
final NsdService service = makeService();
- // Creating an NsdManager will not cause any cmds executed, which means
- // no daemon is started.
+ // Creating an NsdManager will not cause daemon startup.
connectClient(service);
waitForIdle();
- verify(mDaemon, never()).execute(any());
+ verify(mMockMDnsM, never()).registerEventListener(any());
+ verify(mMockMDnsM, never()).startDaemon();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
- // Creating another NsdManager will not cause any cmds executed.
+ // Creating another NsdManager will not cause daemon startup either.
connectClient(service);
waitForIdle();
- verify(mDaemon, never()).execute(any());
+ verify(mMockMDnsM, never()).registerEventListener(any());
+ verify(mMockMDnsM, never()).startDaemon();
final INsdManagerCallback cb2 = getCallback();
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
- // If there is no active request, try to clean up the daemon
- // every time the client disconnects.
+ // If there is no active request, try to clean up the daemon but should not do it because
+ // daemon has not been started.
deathRecipient1.binderDied();
- verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
- reset(mDaemon);
+ verify(mMockMDnsM, never()).unregisterEventListener(any());
+ verify(mMockMDnsM, never()).stopDaemon();
deathRecipient2.binderDied();
- verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ verify(mMockMDnsM, never()).unregisterEventListener(any());
+ verify(mMockMDnsM, never()).stopDaemon();
}
private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
@@ -200,8 +208,8 @@
waitForIdle();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
- verify(mDaemon, never()).maybeStart();
- verify(mDaemon, never()).execute(any());
+ verify(mMockMDnsM, never()).registerEventListener(any());
+ verify(mMockMDnsM, never()).startDaemon();
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
request.setPort(2201);
@@ -210,29 +218,31 @@
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
- verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
+ verify(mMockMDnsM, times(1)).registerEventListener(any());
+ verify(mMockMDnsM, times(1)).startDaemon();
+ verify(mMockMDnsM, times(1)).registerService(
+ eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
// Client discovery request
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
client.discoverServices("a_type", PROTOCOL, listener2);
waitForIdle();
- verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommand("discover 3 a_type 0");
+ verify(mMockMDnsM, times(1)).discover(eq(3), eq("a_type"), eq(0));
// Client resolve request
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
client.resolveService(request, listener3);
waitForIdle();
- verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommand("resolve 4 a_name a_type local. 0");
+ verify(mMockMDnsM, times(1)).resolve(
+ eq(4), eq("a_name"), eq("a_type"), eq("local."), eq(0));
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
deathRecipient.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned
- verifyDaemonCommands("stop-register 2", "stop-discover 3",
- "stop-resolve 4", "stop-service");
+ verify(mMockMDnsM, times(1)).stopOperation(eq(2));
+ verify(mMockMDnsM, times(1)).stopOperation(eq(3));
+ verify(mMockMDnsM, times(1)).stopOperation(eq(4));
}
@Test
@@ -246,20 +256,23 @@
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
- verify(mDaemon, times(1)).maybeStart();
+ verify(mMockMDnsM, times(1)).registerEventListener(any());
+ verify(mMockMDnsM, times(1)).startDaemon();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
- verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
+ verify(mMockMDnsM, times(1)).registerService(
+ eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
client.unregisterService(listener1);
- verifyDaemonCommand("stop-register 2");
+ waitForIdle();
+ verify(mMockMDnsM, times(1)).stopOperation(eq(2));
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
- verifyDaemonCommand("stop-service");
- reset(mDaemon);
+ reset(mMockMDnsM);
deathRecipient.binderDied();
- // Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
- verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ // Client disconnects, daemon should not be stopped after CLEANUP_DELAY_MS.
+ verify(mMockMDnsM, never()).unregisterEventListener(any());
+ verify(mMockMDnsM, never()).stopDaemon();
}
private void waitForIdle() {
@@ -267,11 +280,7 @@
}
NsdService makeService() {
- DaemonConnectionSupplier supplier = (callback) -> {
- mDaemonCallback = callback;
- return mDaemon;
- };
- final NsdService service = new NsdService(mContext, mHandler, supplier, CLEANUP_DELAY_MS) {
+ final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS) {
@Override
public INsdServiceConnector connect(INsdManagerCallback baseCb) {
// Wrap the callback in a transparent mock, to mock asBinder returning a
@@ -285,7 +294,6 @@
return super.connect(cb);
}
};
- verify(mDaemon, never()).execute(any(String.class));
return service;
}
@@ -297,34 +305,15 @@
return new NsdManager(mContext, service);
}
- void verifyDelayMaybeStopDaemon(long cleanupDelayMs) {
+ void verifyDelayMaybeStopDaemon(long cleanupDelayMs) throws Exception {
waitForIdle();
// Stop daemon shouldn't be called immediately.
- verify(mDaemon, never()).maybeStop();
+ verify(mMockMDnsM, never()).unregisterEventListener(any());
+ verify(mMockMDnsM, never()).stopDaemon();
+
// Clean up the daemon after CLEANUP_DELAY_MS.
- verify(mDaemon, timeout(cleanupDelayMs + TIMEOUT_MS)).maybeStop();
- }
-
- void verifyDaemonCommands(String... wants) {
- verifyDaemonCommand(String.join(" ", wants), wants.length);
- }
-
- void verifyDaemonCommand(String want) {
- verifyDaemonCommand(want, 1);
- }
-
- void verifyDaemonCommand(String want, int n) {
- waitForIdle();
- final ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
- verify(mDaemon, times(n)).execute(argumentsCaptor.capture());
- String got = "";
- for (Object o : argumentsCaptor.getAllValues()) {
- got += o + " ";
- }
- assertEquals(want, got.trim());
- // rearm deamon for next command verification
- reset(mDaemon);
- doReturn(true).when(mDaemon).execute(any());
+ verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).unregisterEventListener(any());
+ verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).stopDaemon();
}
public static class TestHandler extends Handler {