Merge "Remove AsyncChannel usage in NsdManager"

This commit is contained in:
Remi NGUYEN VAN
2021-11-22 05:58:15 +00:00
committed by Gerrit Code Review
6 changed files with 509 additions and 255 deletions

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2012, The Android Open Source Project * Copyright (c) 2021, The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,16 +16,15 @@
package android.net.nsd; package android.net.nsd;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
import android.os.Messenger; import android.os.Messenger;
/** /**
* Interface that NsdService implements * Interface that NsdService implements to connect NsdManager clients.
* *
* {@hide} * {@hide}
*/ */
interface INsdManager interface INsdManager {
{ INsdServiceConnector connect(INsdManagerCallback cb);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
Messenger getMessenger();
void setEnabled(boolean enable);
} }

View File

@@ -0,0 +1,39 @@
/**
* 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 android.net.nsd;
import android.os.Messenger;
import android.net.nsd.NsdServiceInfo;
/**
* Callbacks from NsdService to NsdManager
* @hide
*/
oneway interface INsdManagerCallback {
void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info);
void onDiscoverServicesFailed(int listenerKey, int error);
void onServiceFound(int listenerKey, in NsdServiceInfo info);
void onServiceLost(int listenerKey, in NsdServiceInfo info);
void onStopDiscoveryFailed(int listenerKey, int error);
void onStopDiscoverySucceeded(int listenerKey);
void onRegisterServiceFailed(int listenerKey, int error);
void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info);
void onUnregisterServiceFailed(int listenerKey, int error);
void onUnregisterServiceSucceeded(int listenerKey);
void onResolveServiceFailed(int listenerKey, int error);
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
}

View File

@@ -0,0 +1,35 @@
/**
* 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 android.net.nsd;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.NsdServiceInfo;
import android.os.Messenger;
/**
* Interface that NsdService implements for each NsdManager client.
*
* {@hide}
*/
interface INsdServiceConnector {
void registerService(int listenerKey, in NsdServiceInfo serviceInfo);
void unregisterService(int listenerKey);
void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo);
void stopDiscovery(int listenerKey);
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
void startDaemon();
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2021 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -31,17 +31,13 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol; import com.android.internal.util.Protocol;
import java.util.concurrent.CountDownLatch;
/** /**
* The Network Service Discovery Manager class provides the API to discover services * The Network Service Discovery Manager class provides the API to discover services
* on a network. As an example, if device A and device B are connected over a Wi-Fi * on a network. As an example, if device A and device B are connected over a Wi-Fi
@@ -234,6 +230,11 @@ public final class NsdManager {
/** @hide */ /** @hide */
public static final int NATIVE_DAEMON_EVENT = BASE + 26; public static final int NATIVE_DAEMON_EVENT = BASE + 26;
/** @hide */
public static final int REGISTER_CLIENT = BASE + 27;
/** @hide */
public static final int UNREGISTER_CLIENT = BASE + 28;
/** Dns based service discovery protocol */ /** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001; public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -274,7 +275,7 @@ public final class NsdManager {
private static final int FIRST_LISTENER_KEY = 1; private static final int FIRST_LISTENER_KEY = 1;
private final INsdManager mService; private final INsdServiceConnector mService;
private final Context mContext; private final Context mContext;
private int mListenerKey = FIRST_LISTENER_KEY; private int mListenerKey = FIRST_LISTENER_KEY;
@@ -282,9 +283,7 @@ public final class NsdManager {
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
private final Object mMapLock = new Object(); private final Object mMapLock = new Object();
private final AsyncChannel mAsyncChannel = new AsyncChannel(); private final ServiceHandler mHandler;
private ServiceHandler mHandler;
private final CountDownLatch mConnected = new CountDownLatch(1);
/** /**
* Create a new Nsd instance. Applications use * Create a new Nsd instance. Applications use
@@ -295,18 +294,108 @@ public final class NsdManager {
* is a system private class. * is a system private class.
*/ */
public NsdManager(Context context, INsdManager service) { public NsdManager(Context context, INsdManager service) {
mService = service;
mContext = context; mContext = context;
init();
HandlerThread t = new HandlerThread("NsdManager");
t.start();
mHandler = new ServiceHandler(t.getLooper());
try {
mService = service.connect(new NsdCallbackImpl(mHandler));
} catch (RemoteException e) {
throw new RuntimeException("Failed to connect to NsdService");
}
// Only proactively start the daemon if the target SDK < S, otherwise the internal service
// would automatically start/stop the native daemon as needed.
if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
try {
mService.startDaemon();
} catch (RemoteException e) {
Log.e(TAG, "Failed to proactively start daemon");
// Continue: the daemon can still be started on-demand later
}
}
} }
/** private static class NsdCallbackImpl extends INsdManagerCallback.Stub {
* @hide private final Handler mServHandler;
*/
@VisibleForTesting NsdCallbackImpl(Handler serviceHandler) {
public void disconnect() { mServHandler = serviceHandler;
mAsyncChannel.disconnect(); }
mHandler.getLooper().quitSafely();
private void sendInfo(int message, int listenerKey, NsdServiceInfo info) {
mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info));
}
private void sendError(int message, int listenerKey, int error) {
mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey));
}
private void sendNoArg(int message, int listenerKey) {
mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey));
}
@Override
public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info);
}
@Override
public void onDiscoverServicesFailed(int listenerKey, int error) {
sendError(DISCOVER_SERVICES_FAILED, listenerKey, error);
}
@Override
public void onServiceFound(int listenerKey, NsdServiceInfo info) {
sendInfo(SERVICE_FOUND, listenerKey, info);
}
@Override
public void onServiceLost(int listenerKey, NsdServiceInfo info) {
sendInfo(SERVICE_LOST, listenerKey, info);
}
@Override
public void onStopDiscoveryFailed(int listenerKey, int error) {
sendError(STOP_DISCOVERY_FAILED, listenerKey, error);
}
@Override
public void onStopDiscoverySucceeded(int listenerKey) {
sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey);
}
@Override
public void onRegisterServiceFailed(int listenerKey, int error) {
sendError(REGISTER_SERVICE_FAILED, listenerKey, error);
}
@Override
public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info);
}
@Override
public void onUnregisterServiceFailed(int listenerKey, int error) {
sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error);
}
@Override
public void onUnregisterServiceSucceeded(int listenerKey) {
sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey);
}
@Override
public void onResolveServiceFailed(int listenerKey, int error) {
sendError(RESOLVE_SERVICE_FAILED, listenerKey, error);
}
@Override
public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info);
}
} }
/** /**
@@ -376,19 +465,6 @@ public final class NsdManager {
public void handleMessage(Message message) { public void handleMessage(Message message) {
final int what = message.what; final int what = message.what;
final int key = message.arg2; final int key = message.arg2;
switch (what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
return;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
mConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel lost");
return;
default:
break;
}
final Object listener; final Object listener;
final NsdServiceInfo ns; final NsdServiceInfo ns;
synchronized (mMapLock) { synchronized (mMapLock) {
@@ -503,36 +579,6 @@ public final class NsdManager {
return s.getServiceType(); return s.getServiceType();
} }
/**
* Initialize AsyncChannel
*/
private void init() {
final Messenger messenger = getMessenger();
if (messenger == null) {
fatal("Failed to obtain service Messenger");
}
HandlerThread t = new HandlerThread("NsdManager");
t.start();
mHandler = new ServiceHandler(t.getLooper());
mAsyncChannel.connect(mContext, mHandler, messenger);
try {
mConnected.await();
} catch (InterruptedException e) {
fatal("Interrupted wait at init");
}
if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
return;
}
// Only proactively start the daemon if the target SDK < S, otherwise the internal service
// would automatically start/stop the native daemon as needed.
mAsyncChannel.sendMessage(DAEMON_STARTUP);
}
private static void fatal(String msg) {
Log.e(TAG, msg);
throw new RuntimeException(msg);
}
/** /**
* Register a service to be discovered by other services. * Register a service to be discovered by other services.
* *
@@ -556,7 +602,11 @@ public final class NsdManager {
checkServiceInfo(serviceInfo); checkServiceInfo(serviceInfo);
checkProtocol(protocolType); checkProtocol(protocolType);
int key = putListener(listener, serviceInfo); int key = putListener(listener, serviceInfo);
mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); try {
mService.registerService(key, serviceInfo);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
} }
/** /**
@@ -574,7 +624,11 @@ public final class NsdManager {
*/ */
public void unregisterService(RegistrationListener listener) { public void unregisterService(RegistrationListener listener) {
int id = getListenerKey(listener); int id = getListenerKey(listener);
mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); try {
mService.unregisterService(id);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
} }
/** /**
@@ -613,7 +667,11 @@ public final class NsdManager {
s.setServiceType(serviceType); s.setServiceType(serviceType);
int key = putListener(listener, s); int key = putListener(listener, s);
mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); try {
mService.discoverServices(key, s);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
} }
/** /**
@@ -634,7 +692,11 @@ public final class NsdManager {
*/ */
public void stopServiceDiscovery(DiscoveryListener listener) { public void stopServiceDiscovery(DiscoveryListener listener) {
int id = getListenerKey(listener); int id = getListenerKey(listener);
mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); try {
mService.stopDiscovery(id);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
} }
/** /**
@@ -649,29 +711,10 @@ public final class NsdManager {
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
checkServiceInfo(serviceInfo); checkServiceInfo(serviceInfo);
int key = putListener(listener, serviceInfo); int key = putListener(listener, serviceInfo);
mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
}
/** Internal use only @hide */
public void setEnabled(boolean enabled) {
try { try {
mService.setEnabled(enabled); mService.resolveService(key, serviceInfo);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); e.rethrowFromSystemServer();
}
}
/**
* Get a reference to NsdService handler. This is used to establish
* an AsyncChannel communication with the service
*
* @return Messenger pointing to the NsdService handler
*/
private Messenger getMessenger() {
try {
return mService.getMessenger();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} }
} }

View File

@@ -0,0 +1,19 @@
/*
* 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 android.net.nsd;
@JavaOnlyStableParcelable parcelable NsdServiceInfo;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2010 The Android Open Source Project * Copyright (C) 2021 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,24 +20,27 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.NetworkStack;
import android.net.Uri; import android.net.Uri;
import android.net.nsd.INsdManager; import android.net.nsd.INsdManager;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager; import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo; import android.net.nsd.NsdServiceInfo;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils;
import com.android.internal.util.State; import com.android.internal.util.State;
import com.android.internal.util.StateMachine; import com.android.internal.util.StateMachine;
@@ -72,12 +75,11 @@ public class NsdService extends INsdManager.Stub {
/** /**
* Clients receiving asynchronous messages * Clients receiving asynchronous messages
*/ */
private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>(); private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
/* A map from unique id to client info */ /* A map from unique id to client info */
private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
private final AsyncChannel mReplyChannel = new AsyncChannel();
private final long mCleanupDelayMs; private final long mCleanupDelayMs;
private static final int INVALID_ID = 0; private static final int INVALID_ID = 0;
@@ -149,65 +151,66 @@ public class NsdService extends INsdManager.Stub {
class DefaultState extends State { class DefaultState extends State {
@Override @Override
public boolean processMessage(Message msg) { public boolean processMessage(Message msg) {
ClientInfo cInfo = null; final ClientInfo cInfo;
final int clientId = msg.arg2;
switch (msg.what) { switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: case NsdManager.REGISTER_CLIENT:
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { final Pair<NsdServiceConnector, INsdManagerCallback> arg =
AsyncChannel c = (AsyncChannel) msg.obj; (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); final INsdManagerCallback cb = arg.second;
c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); try {
cInfo = new ClientInfo(c, msg.replyTo); cb.asBinder().linkToDeath(arg.first, 0);
mClients.put(msg.replyTo, cInfo); cInfo = new ClientInfo(cb);
} else { mClients.put(arg.first, cInfo);
Slog.e(TAG, "Client connection failure, error=" + msg.arg1); } catch (RemoteException e) {
Log.w(TAG, "Client " + clientId + " has already died");
} }
break; break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: case NsdManager.UNREGISTER_CLIENT:
switch (msg.arg1) { final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
case AsyncChannel.STATUS_SEND_UNSUCCESSFUL: cInfo = mClients.remove(connector);
Slog.e(TAG, "Send failed, client connection lost");
break;
case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
if (DBG) Slog.d(TAG, "Client disconnected");
break;
default:
if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
break;
}
cInfo = mClients.get(msg.replyTo);
if (cInfo != null) { if (cInfo != null) {
cInfo.expungeAllRequests(); cInfo.expungeAllRequests();
mClients.remove(msg.replyTo);
if (cInfo.isLegacy()) { if (cInfo.isLegacy()) {
mLegacyClientCount -= 1; mLegacyClientCount -= 1;
} }
} }
maybeScheduleStop(); maybeScheduleStop();
break; break;
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, getHandler(), msg.replyTo);
break;
case NsdManager.DISCOVER_SERVICES: case NsdManager.DISCOVER_SERVICES:
replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, cInfo = getClientInfoForReply(msg);
NsdManager.FAILURE_INTERNAL_ERROR); if (cInfo != null) {
cInfo.onDiscoverServicesFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break; break;
case NsdManager.STOP_DISCOVERY: case NsdManager.STOP_DISCOVERY:
replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, cInfo = getClientInfoForReply(msg);
NsdManager.FAILURE_INTERNAL_ERROR); if (cInfo != null) {
cInfo.onStopDiscoveryFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break; break;
case NsdManager.REGISTER_SERVICE: case NsdManager.REGISTER_SERVICE:
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, cInfo = getClientInfoForReply(msg);
NsdManager.FAILURE_INTERNAL_ERROR); if (cInfo != null) {
cInfo.onRegisterServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break; break;
case NsdManager.UNREGISTER_SERVICE: case NsdManager.UNREGISTER_SERVICE:
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, cInfo = getClientInfoForReply(msg);
NsdManager.FAILURE_INTERNAL_ERROR); if (cInfo != null) {
cInfo.onUnregisterServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break; break;
case NsdManager.RESOLVE_SERVICE: case NsdManager.RESOLVE_SERVICE:
replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, cInfo = getClientInfoForReply(msg);
NsdManager.FAILURE_INTERNAL_ERROR); if (cInfo != null) {
cInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break; break;
case NsdManager.DAEMON_CLEANUP: case NsdManager.DAEMON_CLEANUP:
mDaemon.maybeStop(); mDaemon.maybeStop();
@@ -215,7 +218,7 @@ public class NsdService extends INsdManager.Stub {
// This event should be only sent by the legacy (target SDK < S) clients. // This event should be only sent by the legacy (target SDK < S) clients.
// Mark the sending client as legacy. // Mark the sending client as legacy.
case NsdManager.DAEMON_STARTUP: case NsdManager.DAEMON_STARTUP:
cInfo = mClients.get(msg.replyTo); cInfo = getClientInfoForReply(msg);
if (cInfo != null) { if (cInfo != null) {
cancelStop(); cancelStop();
cInfo.setLegacy(); cInfo.setLegacy();
@@ -230,6 +233,11 @@ public class NsdService extends INsdManager.Stub {
} }
return HANDLED; return HANDLED;
} }
private ClientInfo getClientInfoForReply(Message msg) {
final ListenerArgs args = (ListenerArgs) msg.obj;
return mClients.get(args.connector);
}
} }
class DisabledState extends State { class DisabledState extends State {
@@ -289,122 +297,119 @@ public class NsdService extends INsdManager.Stub {
@Override @Override
public boolean processMessage(Message msg) { public boolean processMessage(Message msg) {
ClientInfo clientInfo; final ClientInfo clientInfo;
NsdServiceInfo servInfo; final int id;
int id; final int clientId = msg.arg2;
final ListenerArgs args;
switch (msg.what) { switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
return NOT_HANDLED;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
return NOT_HANDLED;
case NsdManager.DISABLE: case NsdManager.DISABLE:
//TODO: cleanup clients //TODO: cleanup clients
transitionTo(mDisabledState); transitionTo(mDisabledState);
break; break;
case NsdManager.DISCOVER_SERVICES: case NsdManager.DISCOVER_SERVICES:
if (DBG) Slog.d(TAG, "Discover services"); if (DBG) Slog.d(TAG, "Discover services");
servInfo = (NsdServiceInfo) msg.obj; args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) { if (requestLimitReached(clientInfo)) {
replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, clientInfo.onDiscoverServicesFailed(
NsdManager.FAILURE_MAX_LIMIT); clientId, NsdManager.FAILURE_MAX_LIMIT);
break; break;
} }
maybeStartDaemon(); maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (discoverServices(id, servInfo.getServiceType())) { if (discoverServices(id, args.serviceInfo.getServiceType())) {
if (DBG) { if (DBG) {
Slog.d(TAG, "Discover " + msg.arg2 + " " + id + Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
servInfo.getServiceType()); args.serviceInfo.getServiceType());
} }
storeRequestMap(msg.arg2, id, clientInfo, msg.what); storeRequestMap(clientId, id, clientInfo, msg.what);
replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo); clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
} else { } else {
stopServiceDiscovery(id); stopServiceDiscovery(id);
replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, clientInfo.onDiscoverServicesFailed(clientId,
NsdManager.FAILURE_INTERNAL_ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.STOP_DISCOVERY: case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery"); if (DBG) Slog.d(TAG, "Stop service discovery");
clientInfo = mClients.get(msg.replyTo); args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
try { try {
id = clientInfo.mClientIds.get(msg.arg2); id = clientInfo.mClientIds.get(clientId);
} catch (NullPointerException e) { } catch (NullPointerException e) {
replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, clientInfo.onStopDiscoveryFailed(
NsdManager.FAILURE_INTERNAL_ERROR); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
} }
removeRequestMap(msg.arg2, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
if (stopServiceDiscovery(id)) { if (stopServiceDiscovery(id)) {
replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); clientInfo.onStopDiscoverySucceeded(clientId);
} else { } else {
replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, clientInfo.onStopDiscoveryFailed(
NsdManager.FAILURE_INTERNAL_ERROR); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.REGISTER_SERVICE: case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service"); if (DBG) Slog.d(TAG, "Register service");
clientInfo = mClients.get(msg.replyTo); args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) { if (requestLimitReached(clientInfo)) {
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, clientInfo.onRegisterServiceFailed(
NsdManager.FAILURE_MAX_LIMIT); clientId, NsdManager.FAILURE_MAX_LIMIT);
break; break;
} }
maybeStartDaemon(); maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (registerService(id, (NsdServiceInfo) msg.obj)) { if (registerService(id, args.serviceInfo)) {
if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id); if (DBG) Slog.d(TAG, "Register " + clientId + " " + id);
storeRequestMap(msg.arg2, id, clientInfo, msg.what); storeRequestMap(clientId, id, clientInfo, msg.what);
// Return success after mDns reports success // Return success after mDns reports success
} else { } else {
unregisterService(id); unregisterService(id);
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, clientInfo.onRegisterServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.UNREGISTER_SERVICE: case NsdManager.UNREGISTER_SERVICE:
if (DBG) Slog.d(TAG, "unregister service"); if (DBG) Slog.d(TAG, "unregister service");
clientInfo = mClients.get(msg.replyTo); args = (ListenerArgs) msg.obj;
try { clientInfo = mClients.get(args.connector);
id = clientInfo.mClientIds.get(msg.arg2); if (clientInfo == null) {
} catch (NullPointerException e) { Slog.e(TAG, "Unknown connector in unregistration");
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
} }
removeRequestMap(msg.arg2, id, clientInfo); id = clientInfo.mClientIds.get(clientId);
removeRequestMap(clientId, id, clientInfo);
if (unregisterService(id)) { if (unregisterService(id)) {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED); clientInfo.onUnregisterServiceSucceeded(clientId);
} else { } else {
replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, clientInfo.onUnregisterServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.RESOLVE_SERVICE: case NsdManager.RESOLVE_SERVICE:
if (DBG) Slog.d(TAG, "Resolve service"); if (DBG) Slog.d(TAG, "Resolve service");
servInfo = (NsdServiceInfo) msg.obj; args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(msg.replyTo); clientInfo = mClients.get(args.connector);
if (clientInfo.mResolvedService != null) { if (clientInfo.mResolvedService != null) {
replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_ALREADY_ACTIVE); clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
break; break;
} }
maybeStartDaemon(); maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (resolveService(id, servInfo)) { if (resolveService(id, args.serviceInfo)) {
clientInfo.mResolvedService = new NsdServiceInfo(); clientInfo.mResolvedService = new NsdServiceInfo();
storeRequestMap(msg.arg2, id, clientInfo, msg.what); storeRequestMap(clientId, id, clientInfo, msg.what);
} else { } else {
replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
} }
break; break;
case NsdManager.NATIVE_DAEMON_EVENT: case NsdManager.NATIVE_DAEMON_EVENT:
@@ -449,30 +454,27 @@ public class NsdService extends INsdManager.Stub {
case NativeResponseCode.SERVICE_FOUND: case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType domain */ /* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]); servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, clientInfo.onServiceFound(clientId, servInfo);
clientId, servInfo);
break; break;
case NativeResponseCode.SERVICE_LOST: case NativeResponseCode.SERVICE_LOST:
/* NNN uniqueId serviceName regType domain */ /* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]); servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, clientInfo.onServiceLost(clientId, servInfo);
clientId, servInfo);
break; break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED: case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */ /* NNN uniqueId errorCode */
clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, clientInfo.onDiscoverServicesFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NativeResponseCode.SERVICE_REGISTERED: case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */ /* NNN regId serviceName regType */
servInfo = new NsdServiceInfo(cooked[2], null); servInfo = new NsdServiceInfo(cooked[2], null);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
id, clientId, servInfo);
break; break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED: case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */ /* NNN regId errorCode */
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, clientInfo.onRegisterServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NativeResponseCode.SERVICE_UPDATED: case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */ /* NNN regId */
@@ -511,8 +513,8 @@ public class NsdService extends INsdManager.Stub {
if (getAddrInfo(id2, cooked[3])) { if (getAddrInfo(id2, cooked[3])) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else { } else {
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientInfo.mResolvedService = null; clientInfo.mResolvedService = null;
} }
break; break;
@@ -521,26 +523,26 @@ public class NsdService extends INsdManager.Stub {
stopResolveService(id); stopResolveService(id);
removeRequestMap(clientId, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null; clientInfo.mResolvedService = null;
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NativeResponseCode.SERVICE_GET_ADDR_FAILED: case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */ /* NNN resolveId errorCode */
stopGetAddrInfo(id); stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null; clientInfo.mResolvedService = null;
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
/* NNN resolveId hostname ttl addr */ /* NNN resolveId hostname ttl addr */
try { try {
clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, clientInfo.onResolveServiceSucceeded(
0, clientId, clientInfo.mResolvedService); clientId, clientInfo.mResolvedService);
} catch (java.net.UnknownHostException e) { } catch (java.net.UnknownHostException e) {
clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_INTERNAL_ERROR, clientId); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
} }
stopGetAddrInfo(id); stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
@@ -601,15 +603,71 @@ public class NsdService extends INsdManager.Stub {
return service; return service;
} }
public Messenger getMessenger() { @Override
public INsdServiceConnector connect(INsdManagerCallback cb) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
return new Messenger(mNsdStateMachine.getHandler()); final INsdServiceConnector connector = new NsdServiceConnector();
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
return connector;
} }
public void setEnabled(boolean isEnabled) { private static class ListenerArgs {
NetworkStack.checkNetworkStackPermission(mContext); public final NsdServiceConnector connector;
mNsdSettings.putEnabledStatus(isEnabled); public final NsdServiceInfo serviceInfo;
notifyEnabled(isEnabled); ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
this.connector = connector;
this.serviceInfo = serviceInfo;
}
}
private class NsdServiceConnector extends INsdServiceConnector.Stub
implements IBinder.DeathRecipient {
@Override
public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.REGISTER_SERVICE, 0, listenerKey,
new ListenerArgs(this, serviceInfo)));
}
@Override
public void unregisterService(int listenerKey) {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
new ListenerArgs(this, null)));
}
@Override
public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.DISCOVER_SERVICES, 0, listenerKey,
new ListenerArgs(this, serviceInfo)));
}
@Override
public void stopDiscovery(int listenerKey) {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
}
@Override
public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.RESOLVE_SERVICE, 0, listenerKey,
new ListenerArgs(this, serviceInfo)));
}
@Override
public void startDaemon() {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
}
@Override
public void binderDied() {
mNsdStateMachine.sendMessage(
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
}
} }
private void notifyEnabled(boolean isEnabled) { private void notifyEnabled(boolean isEnabled) {
@@ -832,43 +890,11 @@ public class NsdService extends INsdManager.Stub {
mNsdStateMachine.dump(fd, pw, args); mNsdStateMachine.dump(fd, pw, args);
} }
/* arg2 on the source message has an id that needs to be retained in replies
* see NsdManager for details */
private Message obtainMessage(Message srcMsg) {
Message msg = Message.obtain();
msg.arg2 = srcMsg.arg2;
return msg;
}
private void replyToMessage(Message msg, int what) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessage(msg);
dstMsg.what = what;
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, int arg1) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessage(msg);
dstMsg.what = what;
dstMsg.arg1 = arg1;
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, Object obj) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessage(msg);
dstMsg.what = what;
dstMsg.obj = obj;
mReplyChannel.replyToMessage(msg, dstMsg);
}
/* Information tracked per client */ /* Information tracked per client */
private class ClientInfo { private class ClientInfo {
private static final int MAX_LIMIT = 10; private static final int MAX_LIMIT = 10;
private final AsyncChannel mChannel; private final INsdManagerCallback mCb;
private final Messenger mMessenger;
/* Remembers a resolved service until getaddrinfo completes */ /* Remembers a resolved service until getaddrinfo completes */
private NsdServiceInfo mResolvedService; private NsdServiceInfo mResolvedService;
@@ -881,17 +907,14 @@ public class NsdService extends INsdManager.Stub {
// The target SDK of this client < Build.VERSION_CODES.S // The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsLegacy = false; private boolean mIsLegacy = false;
private ClientInfo(AsyncChannel c, Messenger m) { private ClientInfo(INsdManagerCallback cb) {
mChannel = c; mCb = cb;
mMessenger = m; if (DBG) Slog.d(TAG, "New client");
if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("mChannel ").append(mChannel).append("\n");
sb.append("mMessenger ").append(mMessenger).append("\n");
sb.append("mResolvedService ").append(mResolvedService).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n");
sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
for(int i = 0; i< mClientIds.size(); i++) { for(int i = 0; i< mClientIds.size(); i++) {
@@ -949,6 +972,102 @@ public class NsdService extends INsdManager.Stub {
} }
return mClientIds.keyAt(idx); return mClientIds.keyAt(idx);
} }
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
try {
mCb.onDiscoverServicesStarted(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
}
}
void onDiscoverServicesFailed(int listenerKey, int error) {
try {
mCb.onDiscoverServicesFailed(listenerKey, error);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
}
}
void onServiceFound(int listenerKey, NsdServiceInfo info) {
try {
mCb.onServiceFound(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onServiceFound(", e);
}
}
void onServiceLost(int listenerKey, NsdServiceInfo info) {
try {
mCb.onServiceLost(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onServiceLost(", e);
}
}
void onStopDiscoveryFailed(int listenerKey, int error) {
try {
mCb.onStopDiscoveryFailed(listenerKey, error);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
}
}
void onStopDiscoverySucceeded(int listenerKey) {
try {
mCb.onStopDiscoverySucceeded(listenerKey);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
}
}
void onRegisterServiceFailed(int listenerKey, int error) {
try {
mCb.onRegisterServiceFailed(listenerKey, error);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onRegisterServiceFailed", e);
}
}
void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
try {
mCb.onRegisterServiceSucceeded(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
}
}
void onUnregisterServiceFailed(int listenerKey, int error) {
try {
mCb.onUnregisterServiceFailed(listenerKey, error);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
}
}
void onUnregisterServiceSucceeded(int listenerKey) {
try {
mCb.onUnregisterServiceSucceeded(listenerKey);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
}
}
void onResolveServiceFailed(int listenerKey, int error) {
try {
mCb.onResolveServiceFailed(listenerKey, error);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onResolveServiceFailed", e);
}
}
void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
try {
mCb.onResolveServiceSucceeded(listenerKey, info);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
}
}
} }
/** /**