Merge changes I4872f8ba,I92039f29,Iaad13e13 am: 767ac317e2
am: 717238a614
Change-Id: Id7ca8a16da5774a00517cfb60b877d181a358353
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
|
||||
package android.net.nsd;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkArgument;
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
|
||||
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.content.Context;
|
||||
@@ -241,12 +245,12 @@ public final class NsdManager {
|
||||
return name;
|
||||
}
|
||||
|
||||
private static int FIRST_LISTENER_KEY = 1;
|
||||
|
||||
private final INsdManager mService;
|
||||
private final Context mContext;
|
||||
|
||||
private static final int INVALID_LISTENER_KEY = 0;
|
||||
private static final int BUSY_LISTENER_KEY = -1;
|
||||
private int mListenerKey = 1;
|
||||
private int mListenerKey = FIRST_LISTENER_KEY;
|
||||
private final SparseArray mListenerMap = new SparseArray();
|
||||
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
|
||||
private final Object mMapLock = new Object();
|
||||
@@ -269,6 +273,14 @@ public final class NsdManager {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void disconnect() {
|
||||
mAsyncChannel.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Failures are passed with {@link RegistrationListener#onRegistrationFailed},
|
||||
* {@link RegistrationListener#onUnregistrationFailed},
|
||||
@@ -304,7 +316,6 @@ public final class NsdManager {
|
||||
public void onServiceFound(NsdServiceInfo serviceInfo);
|
||||
|
||||
public void onServiceLost(NsdServiceInfo serviceInfo);
|
||||
|
||||
}
|
||||
|
||||
/** Interface for callback invocation for service registration */
|
||||
@@ -335,8 +346,9 @@ public final class NsdManager {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (DBG) Log.d(TAG, "received " + nameOf(message.what));
|
||||
switch (message.what) {
|
||||
final int what = message.what;
|
||||
final int key = message.arg2;
|
||||
switch (what) {
|
||||
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
|
||||
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
|
||||
return;
|
||||
@@ -349,19 +361,26 @@ public final class NsdManager {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Object listener = getListener(message.arg2);
|
||||
final Object listener;
|
||||
final NsdServiceInfo ns;
|
||||
synchronized (mMapLock) {
|
||||
listener = mListenerMap.get(key);
|
||||
ns = mServiceMap.get(key);
|
||||
}
|
||||
if (listener == null) {
|
||||
Log.d(TAG, "Stale key " + message.arg2);
|
||||
return;
|
||||
}
|
||||
NsdServiceInfo ns = getNsdService(message.arg2);
|
||||
switch (message.what) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns);
|
||||
}
|
||||
switch (what) {
|
||||
case DISCOVER_SERVICES_STARTED:
|
||||
String s = getNsdServiceInfoType((NsdServiceInfo) message.obj);
|
||||
((DiscoveryListener) listener).onDiscoveryStarted(s);
|
||||
break;
|
||||
case DISCOVER_SERVICES_FAILED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns),
|
||||
message.arg1);
|
||||
break;
|
||||
@@ -374,16 +393,16 @@ public final class NsdManager {
|
||||
case STOP_DISCOVERY_FAILED:
|
||||
// TODO: failure to stop discovery should be internal and retried internally, as
|
||||
// the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns),
|
||||
message.arg1);
|
||||
break;
|
||||
case STOP_DISCOVERY_SUCCEEDED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns));
|
||||
break;
|
||||
case REGISTER_SERVICE_FAILED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1);
|
||||
break;
|
||||
case REGISTER_SERVICE_SUCCEEDED:
|
||||
@@ -391,7 +410,7 @@ public final class NsdManager {
|
||||
(NsdServiceInfo) message.obj);
|
||||
break;
|
||||
case UNREGISTER_SERVICE_FAILED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1);
|
||||
break;
|
||||
case UNREGISTER_SERVICE_SUCCEEDED:
|
||||
@@ -401,11 +420,11 @@ public final class NsdManager {
|
||||
((RegistrationListener) listener).onServiceUnregistered(ns);
|
||||
break;
|
||||
case RESOLVE_SERVICE_FAILED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((ResolveListener) listener).onResolveFailed(ns, message.arg1);
|
||||
break;
|
||||
case RESOLVE_SERVICE_SUCCEEDED:
|
||||
removeListener(message.arg2);
|
||||
removeListener(key);
|
||||
((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj);
|
||||
break;
|
||||
default:
|
||||
@@ -415,40 +434,27 @@ public final class NsdManager {
|
||||
}
|
||||
}
|
||||
|
||||
// if the listener is already in the map, reject it. Otherwise, add it and
|
||||
// return its key.
|
||||
private int nextListenerKey() {
|
||||
// Ensure mListenerKey >= FIRST_LISTENER_KEY;
|
||||
mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1);
|
||||
return mListenerKey;
|
||||
}
|
||||
|
||||
// Assert that the listener is not in the map, then add it and returns its key
|
||||
private int putListener(Object listener, NsdServiceInfo s) {
|
||||
if (listener == null) return INVALID_LISTENER_KEY;
|
||||
int key;
|
||||
checkListener(listener);
|
||||
final int key;
|
||||
synchronized (mMapLock) {
|
||||
int valueIndex = mListenerMap.indexOfValue(listener);
|
||||
if (valueIndex != -1) {
|
||||
return BUSY_LISTENER_KEY;
|
||||
}
|
||||
do {
|
||||
key = mListenerKey++;
|
||||
} while (key == INVALID_LISTENER_KEY);
|
||||
checkArgument(valueIndex == -1, "listener already in use");
|
||||
key = nextListenerKey();
|
||||
mListenerMap.put(key, listener);
|
||||
mServiceMap.put(key, s);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private Object getListener(int key) {
|
||||
if (key == INVALID_LISTENER_KEY) return null;
|
||||
synchronized (mMapLock) {
|
||||
return mListenerMap.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
private NsdServiceInfo getNsdService(int key) {
|
||||
synchronized (mMapLock) {
|
||||
return mServiceMap.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeListener(int key) {
|
||||
if (key == INVALID_LISTENER_KEY) return;
|
||||
synchronized (mMapLock) {
|
||||
mListenerMap.remove(key);
|
||||
mServiceMap.remove(key);
|
||||
@@ -456,16 +462,15 @@ public final class NsdManager {
|
||||
}
|
||||
|
||||
private int getListenerKey(Object listener) {
|
||||
checkListener(listener);
|
||||
synchronized (mMapLock) {
|
||||
int valueIndex = mListenerMap.indexOfValue(listener);
|
||||
if (valueIndex != -1) {
|
||||
return mListenerMap.keyAt(valueIndex);
|
||||
}
|
||||
checkArgument(valueIndex != -1, "listener not registered");
|
||||
return mListenerMap.keyAt(valueIndex);
|
||||
}
|
||||
return INVALID_LISTENER_KEY;
|
||||
}
|
||||
|
||||
private String getNsdServiceInfoType(NsdServiceInfo s) {
|
||||
private static String getNsdServiceInfoType(NsdServiceInfo s) {
|
||||
if (s == null) return "?";
|
||||
return s.getServiceType();
|
||||
}
|
||||
@@ -475,7 +480,9 @@ public final class NsdManager {
|
||||
*/
|
||||
private void init() {
|
||||
final Messenger messenger = getMessenger();
|
||||
if (messenger == null) throw new RuntimeException("Failed to initialize");
|
||||
if (messenger == null) {
|
||||
fatal("Failed to obtain service Messenger");
|
||||
}
|
||||
HandlerThread t = new HandlerThread("NsdManager");
|
||||
t.start();
|
||||
mHandler = new ServiceHandler(t.getLooper());
|
||||
@@ -483,10 +490,15 @@ public final class NsdManager {
|
||||
try {
|
||||
mConnected.await();
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "interrupted wait at init");
|
||||
fatal("Interrupted wait at init");
|
||||
}
|
||||
}
|
||||
|
||||
private static void fatal(String msg) {
|
||||
Log.e(TAG, msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a service to be discovered by other services.
|
||||
*
|
||||
@@ -506,23 +518,10 @@ public final class NsdManager {
|
||||
*/
|
||||
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
|
||||
RegistrationListener listener) {
|
||||
if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
|
||||
TextUtils.isEmpty(serviceInfo.getServiceType())) {
|
||||
throw new IllegalArgumentException("Service name or type cannot be empty");
|
||||
}
|
||||
if (serviceInfo.getPort() <= 0) {
|
||||
throw new IllegalArgumentException("Invalid port number");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
if (protocolType != PROTOCOL_DNS_SD) {
|
||||
throw new IllegalArgumentException("Unsupported protocol");
|
||||
}
|
||||
checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
|
||||
checkServiceInfo(serviceInfo);
|
||||
checkProtocol(protocolType);
|
||||
int key = putListener(listener, serviceInfo);
|
||||
if (key == BUSY_LISTENER_KEY) {
|
||||
throw new IllegalArgumentException("listener already in use");
|
||||
}
|
||||
mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
|
||||
}
|
||||
|
||||
@@ -541,12 +540,6 @@ public final class NsdManager {
|
||||
*/
|
||||
public void unregisterService(RegistrationListener listener) {
|
||||
int id = getListenerKey(listener);
|
||||
if (id == INVALID_LISTENER_KEY) {
|
||||
throw new IllegalArgumentException("listener not registered");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
|
||||
}
|
||||
|
||||
@@ -579,25 +572,13 @@ public final class NsdManager {
|
||||
* Cannot be null. Cannot be in use for an active service discovery.
|
||||
*/
|
||||
public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
if (TextUtils.isEmpty(serviceType)) {
|
||||
throw new IllegalArgumentException("Service type cannot be empty");
|
||||
}
|
||||
|
||||
if (protocolType != PROTOCOL_DNS_SD) {
|
||||
throw new IllegalArgumentException("Unsupported protocol");
|
||||
}
|
||||
checkStringNotEmpty(serviceType, "Service type cannot be empty");
|
||||
checkProtocol(protocolType);
|
||||
|
||||
NsdServiceInfo s = new NsdServiceInfo();
|
||||
s.setServiceType(serviceType);
|
||||
|
||||
int key = putListener(listener, s);
|
||||
if (key == BUSY_LISTENER_KEY) {
|
||||
throw new IllegalArgumentException("listener already in use");
|
||||
}
|
||||
|
||||
mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s);
|
||||
}
|
||||
|
||||
@@ -619,12 +600,6 @@ public final class NsdManager {
|
||||
*/
|
||||
public void stopServiceDiscovery(DiscoveryListener listener) {
|
||||
int id = getListenerKey(listener);
|
||||
if (id == INVALID_LISTENER_KEY) {
|
||||
throw new IllegalArgumentException("service discovery not active on listener");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
|
||||
}
|
||||
|
||||
@@ -638,19 +613,8 @@ public final class NsdManager {
|
||||
* Cannot be in use for an active service resolution.
|
||||
*/
|
||||
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
|
||||
if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
|
||||
TextUtils.isEmpty(serviceInfo.getServiceType())) {
|
||||
throw new IllegalArgumentException("Service name or type cannot be empty");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
|
||||
checkServiceInfo(serviceInfo);
|
||||
int key = putListener(listener, serviceInfo);
|
||||
|
||||
if (key == BUSY_LISTENER_KEY) {
|
||||
throw new IllegalArgumentException("listener already in use");
|
||||
}
|
||||
mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
|
||||
}
|
||||
|
||||
@@ -664,10 +628,10 @@ public final class NsdManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to NetworkService handler. This is used to establish
|
||||
* Get a reference to NsdService handler. This is used to establish
|
||||
* an AsyncChannel communication with the service
|
||||
*
|
||||
* @return Messenger pointing to the NetworkService handler
|
||||
* @return Messenger pointing to the NsdService handler
|
||||
*/
|
||||
private Messenger getMessenger() {
|
||||
try {
|
||||
@@ -676,4 +640,18 @@ public final class NsdManager {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkListener(Object listener) {
|
||||
checkNotNull(listener, "listener cannot be null");
|
||||
}
|
||||
|
||||
private static void checkProtocol(int protocolType) {
|
||||
checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol");
|
||||
}
|
||||
|
||||
private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
|
||||
checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
|
||||
checkStringNotEmpty(serviceInfo.getServiceName(),"Service name cannot be empty");
|
||||
checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.provider.Settings;
|
||||
import android.util.Base64;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -48,7 +49,6 @@ import com.android.internal.util.AsyncChannel;
|
||||
import com.android.internal.util.Protocol;
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.StateMachine;
|
||||
import com.android.server.NativeDaemonConnector.Command;
|
||||
|
||||
/**
|
||||
* Network Service Discovery Service handles remote service discovery operation requests by
|
||||
@@ -161,7 +161,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
}
|
||||
//Last client
|
||||
if (mClients.size() == 0) {
|
||||
stopMDnsDaemon();
|
||||
mDaemon.stop();
|
||||
}
|
||||
break;
|
||||
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
|
||||
@@ -221,14 +221,14 @@ public class NsdService extends INsdManager.Stub {
|
||||
public void enter() {
|
||||
sendNsdStateChangeBroadcast(true);
|
||||
if (mClients.size() > 0) {
|
||||
startMDnsDaemon();
|
||||
mDaemon.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
if (mClients.size() > 0) {
|
||||
stopMDnsDaemon();
|
||||
mDaemon.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,8 +247,8 @@ public class NsdService extends INsdManager.Stub {
|
||||
}
|
||||
|
||||
private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
|
||||
clientInfo.mClientIds.remove(clientId);
|
||||
clientInfo.mClientRequests.remove(clientId);
|
||||
clientInfo.mClientIds.delete(clientId);
|
||||
clientInfo.mClientRequests.delete(clientId);
|
||||
mIdToClientInfoMap.remove(globalId);
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
//First client
|
||||
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
|
||||
mClients.size() == 0) {
|
||||
startMDnsDaemon();
|
||||
mDaemon.start();
|
||||
}
|
||||
return NOT_HANDLED;
|
||||
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
|
||||
@@ -301,7 +301,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
clientInfo = mClients.get(msg.replyTo);
|
||||
|
||||
try {
|
||||
id = clientInfo.mClientIds.get(msg.arg2).intValue();
|
||||
id = clientInfo.mClientIds.get(msg.arg2);
|
||||
} catch (NullPointerException e) {
|
||||
replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
|
||||
NsdManager.FAILURE_INTERNAL_ERROR);
|
||||
@@ -339,7 +339,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
if (DBG) Slog.d(TAG, "unregister service");
|
||||
clientInfo = mClients.get(msg.replyTo);
|
||||
try {
|
||||
id = clientInfo.mClientIds.get(msg.arg2).intValue();
|
||||
id = clientInfo.mClientIds.get(msg.arg2);
|
||||
} catch (NullPointerException e) {
|
||||
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
|
||||
NsdManager.FAILURE_INTERNAL_ERROR);
|
||||
@@ -712,26 +712,13 @@ public class NsdService extends INsdManager.Stub {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean execute(Command cmd) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, cmd.toString());
|
||||
}
|
||||
try {
|
||||
mNativeConnector.execute(cmd);
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
Slog.e(TAG, "Failed to execute " + cmd, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
public void start() {
|
||||
execute("start-service");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startMDnsDaemon() {
|
||||
return mDaemon.execute("start-service");
|
||||
}
|
||||
|
||||
private boolean stopMDnsDaemon() {
|
||||
return mDaemon.execute("stop-service");
|
||||
public void stop() {
|
||||
execute("stop-service");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean registerService(int regId, NsdServiceInfo service) {
|
||||
@@ -743,8 +730,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
int port = service.getPort();
|
||||
byte[] textRecord = service.getTxtRecord();
|
||||
String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
|
||||
Command cmd = new Command("mdnssd", "register", regId, name, type, port, record);
|
||||
return mDaemon.execute(cmd);
|
||||
return mDaemon.execute("register", regId, name, type, port, record);
|
||||
}
|
||||
|
||||
private boolean unregisterService(int regId) {
|
||||
@@ -843,10 +829,10 @@ public class NsdService extends INsdManager.Stub {
|
||||
private NsdServiceInfo mResolvedService;
|
||||
|
||||
/* A map from client id to unique id sent to mDns */
|
||||
private final SparseArray<Integer> mClientIds = new SparseArray<>();
|
||||
private final SparseIntArray mClientIds = new SparseIntArray();
|
||||
|
||||
/* A map from client id to the type of the request we had received */
|
||||
private final SparseArray<Integer> mClientRequests = new SparseArray<>();
|
||||
private final SparseIntArray mClientRequests = new SparseIntArray();
|
||||
|
||||
private ClientInfo(AsyncChannel c, Messenger m) {
|
||||
mChannel = c;
|
||||
@@ -873,6 +859,7 @@ public class NsdService extends INsdManager.Stub {
|
||||
// and send cancellations to the daemon.
|
||||
private void expungeAllRequests() {
|
||||
int globalId, clientId, i;
|
||||
// TODO: to keep handler responsive, do not clean all requests for that client at once.
|
||||
for (i = 0; i < mClientIds.size(); i++) {
|
||||
clientId = mClientIds.keyAt(i);
|
||||
globalId = mClientIds.valueAt(i);
|
||||
@@ -900,15 +887,11 @@ public class NsdService extends INsdManager.Stub {
|
||||
// mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
|
||||
// return the corresponding listener id. mDnsClient id is also called a global id.
|
||||
private int getClientId(final int globalId) {
|
||||
// This doesn't use mClientIds.indexOfValue because indexOfValue uses == (not .equals)
|
||||
// while also coercing the int primitives to Integer objects.
|
||||
for (int i = 0, nSize = mClientIds.size(); i < nSize; i++) {
|
||||
int mDnsId = mClientIds.valueAt(i);
|
||||
if (globalId == mDnsId) {
|
||||
return mClientIds.keyAt(i);
|
||||
}
|
||||
int idx = mClientIds.indexOfValue(globalId);
|
||||
if (idx < 0) {
|
||||
return idx;
|
||||
}
|
||||
return -1;
|
||||
return mClientIds.keyAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,68 +16,121 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.test.TestLooper;
|
||||
import android.content.Context;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import com.android.server.NsdService.DaemonConnection;
|
||||
import com.android.server.NsdService.DaemonConnectionSupplier;
|
||||
import com.android.server.NsdService.NativeCallbackReceiver;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import org.junit.After;
|
||||
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;
|
||||
|
||||
// TODOs:
|
||||
// - test client disconnects
|
||||
// - test client can send requests and receive replies
|
||||
// - test NSD_ON ENABLE/DISABLED listening
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class NsdServiceTest {
|
||||
|
||||
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
|
||||
|
||||
long mTimeoutMs = 100; // non-final so that tests can adjust the value.
|
||||
|
||||
@Mock Context mContext;
|
||||
@Mock ContentResolver mResolver;
|
||||
@Mock NsdService.NsdSettings mSettings;
|
||||
@Mock DaemonConnection mDaemon;
|
||||
NativeCallbackReceiver mDaemonCallback;
|
||||
TestLooper mLooper;
|
||||
HandlerThread mThread;
|
||||
TestHandler mHandler;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mLooper = new TestLooper();
|
||||
mHandler = new TestHandler(mLooper.getLooper());
|
||||
mThread = new HandlerThread("mock-service-handler");
|
||||
mThread.start();
|
||||
mHandler = new TestHandler(mThread.getLooper());
|
||||
when(mContext.getContentResolver()).thenReturn(mResolver);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mThread.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientsCanConnect() {
|
||||
public void testClientsCanConnectAndDisconnect() {
|
||||
when(mSettings.isEnabled()).thenReturn(true);
|
||||
|
||||
NsdService service = makeService();
|
||||
|
||||
NsdManager client1 = connectClient(service);
|
||||
verify(mDaemon, timeout(100).times(1)).execute("start-service");
|
||||
verify(mDaemon, timeout(100).times(1)).start();
|
||||
|
||||
NsdManager client2 = connectClient(service);
|
||||
|
||||
// TODO: disconnect client1
|
||||
// TODO: disconnect client2
|
||||
client1.disconnect();
|
||||
client2.disconnect();
|
||||
|
||||
verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientRequestsAreGCedAtDisconnection() {
|
||||
when(mSettings.isEnabled()).thenReturn(true);
|
||||
when(mDaemon.execute(any())).thenReturn(true);
|
||||
|
||||
NsdService service = makeService();
|
||||
NsdManager client = connectClient(service);
|
||||
|
||||
verify(mDaemon, timeout(100).times(1)).start();
|
||||
|
||||
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
|
||||
request.setPort(2201);
|
||||
|
||||
// Client registration request
|
||||
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
|
||||
client.registerService(request, PROTOCOL, listener1);
|
||||
verifyDaemonCommand("register 2 a_name a_type 2201");
|
||||
|
||||
// Client discovery request
|
||||
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
|
||||
client.discoverServices("a_type", PROTOCOL, listener2);
|
||||
verifyDaemonCommand("discover 3 a_type");
|
||||
|
||||
// Client resolve request
|
||||
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
|
||||
client.resolveService(request, listener3);
|
||||
verifyDaemonCommand("resolve 4 a_name a_type local.");
|
||||
|
||||
// Client disconnects
|
||||
client.disconnect();
|
||||
verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
|
||||
|
||||
// checks that request are cleaned
|
||||
verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
|
||||
}
|
||||
|
||||
NsdService makeService() {
|
||||
@@ -91,10 +144,28 @@ public class NsdServiceTest {
|
||||
}
|
||||
|
||||
NsdManager connectClient(NsdService service) {
|
||||
mLooper.startAutoDispatch();
|
||||
NsdManager client = new NsdManager(mContext, service);
|
||||
mLooper.stopAutoDispatch();
|
||||
return client;
|
||||
return new NsdManager(mContext, service);
|
||||
}
|
||||
|
||||
void verifyDaemonCommands(String... wants) {
|
||||
verifyDaemonCommand(String.join(" ", wants), wants.length);
|
||||
}
|
||||
|
||||
void verifyDaemonCommand(String want) {
|
||||
verifyDaemonCommand(want, 1);
|
||||
}
|
||||
|
||||
void verifyDaemonCommand(String want, int n) {
|
||||
ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
|
||||
verify(mDaemon, timeout(mTimeoutMs).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);
|
||||
when(mDaemon.execute(any())).thenReturn(true);
|
||||
}
|
||||
|
||||
public static class TestHandler extends Handler {
|
||||
|
||||
Reference in New Issue
Block a user