diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 27ca6e2e51..3fd9f19364 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -16,10 +16,6 @@ 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; @@ -245,12 +241,12 @@ public final class NsdManager { return name; } - private static int FIRST_LISTENER_KEY = 1; - private final INsdManager mService; private final Context mContext; - private int mListenerKey = FIRST_LISTENER_KEY; + private static final int INVALID_LISTENER_KEY = 0; + private static final int BUSY_LISTENER_KEY = -1; + private int mListenerKey = 1; private final SparseArray mListenerMap = new SparseArray(); private final SparseArray mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); @@ -273,14 +269,6 @@ public final class NsdManager { init(); } - /** - * @hide - */ - @VisibleForTesting - public void disconnect() { - mAsyncChannel.disconnect(); - } - /** * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, * {@link RegistrationListener#onUnregistrationFailed}, @@ -316,6 +304,7 @@ public final class NsdManager { public void onServiceFound(NsdServiceInfo serviceInfo); public void onServiceLost(NsdServiceInfo serviceInfo); + } /** Interface for callback invocation for service registration */ @@ -346,9 +335,8 @@ public final class NsdManager { @Override public void handleMessage(Message message) { - final int what = message.what; - final int key = message.arg2; - switch (what) { + if (DBG) Log.d(TAG, "received " + nameOf(message.what)); + switch (message.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); return; @@ -361,26 +349,19 @@ public final class NsdManager { default: break; } - final Object listener; - final NsdServiceInfo ns; - synchronized (mMapLock) { - listener = mListenerMap.get(key); - ns = mServiceMap.get(key); - } + Object listener = getListener(message.arg2); if (listener == null) { Log.d(TAG, "Stale key " + message.arg2); return; } - if (DBG) { - Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns); - } - switch (what) { + NsdServiceInfo ns = getNsdService(message.arg2); + switch (message.what) { case DISCOVER_SERVICES_STARTED: String s = getNsdServiceInfoType((NsdServiceInfo) message.obj); ((DiscoveryListener) listener).onDiscoveryStarted(s); break; case DISCOVER_SERVICES_FAILED: - removeListener(key); + removeListener(message.arg2); ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; @@ -393,16 +374,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(key); + removeListener(message.arg2); ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; case STOP_DISCOVERY_SUCCEEDED: - removeListener(key); + removeListener(message.arg2); ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns)); break; case REGISTER_SERVICE_FAILED: - removeListener(key); + removeListener(message.arg2); ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1); break; case REGISTER_SERVICE_SUCCEEDED: @@ -410,7 +391,7 @@ public final class NsdManager { (NsdServiceInfo) message.obj); break; case UNREGISTER_SERVICE_FAILED: - removeListener(key); + removeListener(message.arg2); ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1); break; case UNREGISTER_SERVICE_SUCCEEDED: @@ -420,11 +401,11 @@ public final class NsdManager { ((RegistrationListener) listener).onServiceUnregistered(ns); break; case RESOLVE_SERVICE_FAILED: - removeListener(key); + removeListener(message.arg2); ((ResolveListener) listener).onResolveFailed(ns, message.arg1); break; case RESOLVE_SERVICE_SUCCEEDED: - removeListener(key); + removeListener(message.arg2); ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj); break; default: @@ -434,27 +415,40 @@ public final class NsdManager { } } - 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 + // if the listener is already in the map, reject it. Otherwise, add it and + // return its key. private int putListener(Object listener, NsdServiceInfo s) { - checkListener(listener); - final int key; + if (listener == null) return INVALID_LISTENER_KEY; + int key; synchronized (mMapLock) { int valueIndex = mListenerMap.indexOfValue(listener); - checkArgument(valueIndex == -1, "listener already in use"); - key = nextListenerKey(); + if (valueIndex != -1) { + return BUSY_LISTENER_KEY; + } + do { + key = mListenerKey++; + } while (key == INVALID_LISTENER_KEY); 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); @@ -462,15 +456,16 @@ public final class NsdManager { } private int getListenerKey(Object listener) { - checkListener(listener); synchronized (mMapLock) { int valueIndex = mListenerMap.indexOfValue(listener); - checkArgument(valueIndex != -1, "listener not registered"); - return mListenerMap.keyAt(valueIndex); + if (valueIndex != -1) { + return mListenerMap.keyAt(valueIndex); + } } + return INVALID_LISTENER_KEY; } - private static String getNsdServiceInfoType(NsdServiceInfo s) { + private String getNsdServiceInfoType(NsdServiceInfo s) { if (s == null) return "?"; return s.getServiceType(); } @@ -480,9 +475,7 @@ public final class NsdManager { */ private void init() { final Messenger messenger = getMessenger(); - if (messenger == null) { - fatal("Failed to obtain service Messenger"); - } + if (messenger == null) throw new RuntimeException("Failed to initialize"); HandlerThread t = new HandlerThread("NsdManager"); t.start(); mHandler = new ServiceHandler(t.getLooper()); @@ -490,15 +483,10 @@ public final class NsdManager { try { mConnected.await(); } catch (InterruptedException e) { - fatal("Interrupted wait at init"); + Log.e(TAG, "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. * @@ -518,10 +506,23 @@ public final class NsdManager { */ public void registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener) { - checkArgument(serviceInfo.getPort() > 0, "Invalid port number"); - checkServiceInfo(serviceInfo); - checkProtocol(protocolType); + 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"); + } int key = putListener(listener, serviceInfo); + if (key == BUSY_LISTENER_KEY) { + throw new IllegalArgumentException("listener already in use"); + } mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); } @@ -540,6 +541,12 @@ 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); } @@ -572,13 +579,25 @@ 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) { - checkStringNotEmpty(serviceType, "Service type cannot be empty"); - checkProtocol(protocolType); + 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"); + } 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); } @@ -600,6 +619,12 @@ 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); } @@ -613,8 +638,19 @@ public final class NsdManager { * Cannot be in use for an active service resolution. */ public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { - checkServiceInfo(serviceInfo); + 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"); + } + int key = putListener(listener, serviceInfo); + + if (key == BUSY_LISTENER_KEY) { + throw new IllegalArgumentException("listener already in use"); + } mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); } @@ -628,10 +664,10 @@ public final class NsdManager { } /** - * Get a reference to NsdService handler. This is used to establish + * Get a reference to NetworkService handler. This is used to establish * an AsyncChannel communication with the service * - * @return Messenger pointing to the NsdService handler + * @return Messenger pointing to the NetworkService handler */ private Messenger getMessenger() { try { @@ -640,18 +676,4 @@ 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"); - } } diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index 7a560bb878..d74bc19f74 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -35,7 +35,6 @@ 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; @@ -50,6 +49,7 @@ import com.android.internal.util.DumpUtils; 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 @@ -162,7 +162,7 @@ public class NsdService extends INsdManager.Stub { } //Last client if (mClients.size() == 0) { - mDaemon.stop(); + stopMDnsDaemon(); } break; case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: @@ -222,14 +222,14 @@ public class NsdService extends INsdManager.Stub { public void enter() { sendNsdStateChangeBroadcast(true); if (mClients.size() > 0) { - mDaemon.start(); + startMDnsDaemon(); } } @Override public void exit() { if (mClients.size() > 0) { - mDaemon.stop(); + stopMDnsDaemon(); } } @@ -248,8 +248,8 @@ public class NsdService extends INsdManager.Stub { } private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { - clientInfo.mClientIds.delete(clientId); - clientInfo.mClientRequests.delete(clientId); + clientInfo.mClientIds.remove(clientId); + clientInfo.mClientRequests.remove(clientId); mIdToClientInfoMap.remove(globalId); } @@ -263,7 +263,7 @@ public class NsdService extends INsdManager.Stub { //First client if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL && mClients.size() == 0) { - mDaemon.start(); + startMDnsDaemon(); } return NOT_HANDLED; case AsyncChannel.CMD_CHANNEL_DISCONNECTED: @@ -302,7 +302,7 @@ public class NsdService extends INsdManager.Stub { clientInfo = mClients.get(msg.replyTo); try { - id = clientInfo.mClientIds.get(msg.arg2); + id = clientInfo.mClientIds.get(msg.arg2).intValue(); } catch (NullPointerException e) { replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, NsdManager.FAILURE_INTERNAL_ERROR); @@ -340,7 +340,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); + id = clientInfo.mClientIds.get(msg.arg2).intValue(); } catch (NullPointerException e) { replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, NsdManager.FAILURE_INTERNAL_ERROR); @@ -713,13 +713,26 @@ public class NsdService extends INsdManager.Stub { return true; } - public void start() { - execute("start-service"); + 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 stop() { - execute("stop-service"); - } + private boolean startMDnsDaemon() { + return mDaemon.execute("start-service"); + } + + private boolean stopMDnsDaemon() { + return mDaemon.execute("stop-service"); } private boolean registerService(int regId, NsdServiceInfo service) { @@ -731,7 +744,8 @@ public class NsdService extends INsdManager.Stub { 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); + Command cmd = new Command("mdnssd", "register", regId, name, type, port, record); + return mDaemon.execute(cmd); } private boolean unregisterService(int regId) { @@ -824,10 +838,10 @@ public class NsdService extends INsdManager.Stub { private NsdServiceInfo mResolvedService; /* A map from client id to unique id sent to mDns */ - private final SparseIntArray mClientIds = new SparseIntArray(); + private final SparseArray mClientIds = new SparseArray<>(); /* A map from client id to the type of the request we had received */ - private final SparseIntArray mClientRequests = new SparseIntArray(); + private final SparseArray mClientRequests = new SparseArray<>(); private ClientInfo(AsyncChannel c, Messenger m) { mChannel = c; @@ -854,7 +868,6 @@ 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); @@ -882,11 +895,15 @@ 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) { - int idx = mClientIds.indexOfValue(globalId); - if (idx < 0) { - return idx; + // 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); + } } - return mClientIds.keyAt(idx); + return -1; } }