Make NsdService only start the native daemon when needed and automatically clean it up.

Currently, NsdService starts the native mdnsresponder daemon if any
NsdManager connect to it, which results in that when any constant
service holds the NsdManager connection, the device would always be
in the mdns multicast group whatever the connection is not used or not.
This is because mdnsresponder will join the multicast group when it
starts.

To solve this problem, start the native daemon only when needed, and
clean it up after the given idle timeout.

1. Start the native daemon when a new request come.
2. If there is no pending request, clean up the daemon after 3 seconds
of idle time.

Bug: 181810560
Test: atest NsdManagerTest NsdServiceTest
Change-Id: I3eb04552f6cf6c0c68c07abffe751bb4d0669215
This commit is contained in:
Luke Huang
2021-05-26 10:17:46 +08:00
parent acbea9a48e
commit 17df73a1b8
2 changed files with 66 additions and 19 deletions

View File

@@ -199,6 +199,9 @@ public final class NsdManager {
/** @hide */ /** @hide */
public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20;
/** @hide */
public static final int DAEMON_CLEANUP = BASE + 21;
/** @hide */ /** @hide */
public static final int ENABLE = BASE + 24; public static final int ENABLE = BASE + 24;
/** @hide */ /** @hide */

View File

@@ -61,6 +61,7 @@ public class NsdService extends INsdManager.Stub {
private static final String MDNS_TAG = "mDnsConnector"; private static final String MDNS_TAG = "mDnsConnector";
private static final boolean DBG = true; private static final boolean DBG = true;
private static final long CLEANUP_DELAY_MS = 3000;
private final Context mContext; private final Context mContext;
private final NsdSettings mNsdSettings; private final NsdSettings mNsdSettings;
@@ -77,6 +78,7 @@ public class NsdService extends INsdManager.Stub {
private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
private final AsyncChannel mReplyChannel = new AsyncChannel(); private final AsyncChannel mReplyChannel = new AsyncChannel();
private final long mCleanupDelayMs;
private static final int INVALID_ID = 0; private static final int INVALID_ID = 0;
private int mUniqueId = 1; private int mUniqueId = 1;
@@ -92,6 +94,22 @@ public class NsdService extends INsdManager.Stub {
return NsdManager.nameOf(what); return NsdManager.nameOf(what);
} }
void maybeStartDaemon() {
mDaemon.maybeStart();
maybeScheduleStop();
}
void maybeScheduleStop() {
if (!isAnyRequestActive()) {
cancelStop();
sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
}
}
void cancelStop() {
this.removeMessages(NsdManager.DAEMON_CLEANUP);
}
/** /**
* Observes the NSD on/off setting, and takes action when changed. * Observes the NSD on/off setting, and takes action when changed.
*/ */
@@ -151,10 +169,6 @@ public class NsdService extends INsdManager.Stub {
cInfo.expungeAllRequests(); cInfo.expungeAllRequests();
mClients.remove(msg.replyTo); mClients.remove(msg.replyTo);
} }
//Last client
if (mClients.size() == 0) {
mDaemon.stop();
}
break; break;
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
AsyncChannel ac = new AsyncChannel(); AsyncChannel ac = new AsyncChannel();
@@ -180,6 +194,9 @@ public class NsdService extends INsdManager.Stub {
replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR); NsdManager.FAILURE_INTERNAL_ERROR);
break; break;
case NsdManager.DAEMON_CLEANUP:
mDaemon.maybeStop();
break;
case NsdManager.NATIVE_DAEMON_EVENT: case NsdManager.NATIVE_DAEMON_EVENT:
default: default:
Slog.e(TAG, "Unhandled " + msg); Slog.e(TAG, "Unhandled " + msg);
@@ -212,16 +229,13 @@ public class NsdService extends INsdManager.Stub {
@Override @Override
public void enter() { public void enter() {
sendNsdStateChangeBroadcast(true); sendNsdStateChangeBroadcast(true);
if (mClients.size() > 0) {
mDaemon.start();
}
} }
@Override @Override
public void exit() { public void exit() {
if (mClients.size() > 0) { // TODO: it is incorrect to stop the daemon without expunging all requests
mDaemon.stop(); // and sending error callbacks to clients.
} maybeScheduleStop();
} }
private boolean requestLimitReached(ClientInfo clientInfo) { private boolean requestLimitReached(ClientInfo clientInfo) {
@@ -236,12 +250,15 @@ public class NsdService extends INsdManager.Stub {
clientInfo.mClientIds.put(clientId, globalId); clientInfo.mClientIds.put(clientId, globalId);
clientInfo.mClientRequests.put(clientId, what); clientInfo.mClientRequests.put(clientId, what);
mIdToClientInfoMap.put(globalId, clientInfo); mIdToClientInfoMap.put(globalId, clientInfo);
// Remove the cleanup event because here comes a new request.
cancelStop();
} }
private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
clientInfo.mClientIds.delete(clientId); clientInfo.mClientIds.delete(clientId);
clientInfo.mClientRequests.delete(clientId); clientInfo.mClientRequests.delete(clientId);
mIdToClientInfoMap.remove(globalId); mIdToClientInfoMap.remove(globalId);
maybeScheduleStop();
} }
@Override @Override
@@ -251,14 +268,12 @@ public class NsdService extends INsdManager.Stub {
int id; int id;
switch (msg.what) { switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
//First client
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
mClients.size() == 0) {
mDaemon.start();
}
return NOT_HANDLED; return NOT_HANDLED;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
return NOT_HANDLED; return NOT_HANDLED;
}
switch (msg.what) {
case NsdManager.DISABLE: case NsdManager.DISABLE:
//TODO: cleanup clients //TODO: cleanup clients
transitionTo(mDisabledState); transitionTo(mDisabledState);
@@ -274,6 +289,7 @@ public class NsdService extends INsdManager.Stub {
break; break;
} }
maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (discoverServices(id, servInfo.getServiceType())) { if (discoverServices(id, servInfo.getServiceType())) {
if (DBG) { if (DBG) {
@@ -316,6 +332,7 @@ public class NsdService extends INsdManager.Stub {
break; break;
} }
maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (registerService(id, (NsdServiceInfo) msg.obj)) { if (registerService(id, (NsdServiceInfo) msg.obj)) {
if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id); if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
@@ -357,6 +374,7 @@ public class NsdService extends INsdManager.Stub {
break; break;
} }
maybeStartDaemon();
id = getUniqueId(); id = getUniqueId();
if (resolveService(id, servInfo)) { if (resolveService(id, servInfo)) {
clientInfo.mResolvedService = new NsdServiceInfo(); clientInfo.mResolvedService = new NsdServiceInfo();
@@ -513,6 +531,10 @@ public class NsdService extends INsdManager.Stub {
} }
} }
private boolean isAnyRequestActive() {
return mIdToClientInfoMap.size() != 0;
}
private String unescape(String s) { private String unescape(String s) {
StringBuilder sb = new StringBuilder(s.length()); StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); ++i) { for (int i = 0; i < s.length(); ++i) {
@@ -538,7 +560,9 @@ public class NsdService extends INsdManager.Stub {
} }
@VisibleForTesting @VisibleForTesting
NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) { NsdService(Context ctx, NsdSettings settings, Handler handler,
DaemonConnectionSupplier fn, long cleanupDelayMs) {
mCleanupDelayMs = cleanupDelayMs;
mContext = ctx; mContext = ctx;
mNsdSettings = settings; mNsdSettings = settings;
mNsdStateMachine = new NsdStateMachine(TAG, handler); mNsdStateMachine = new NsdStateMachine(TAG, handler);
@@ -552,7 +576,8 @@ public class NsdService extends INsdManager.Stub {
HandlerThread thread = new HandlerThread(TAG); HandlerThread thread = new HandlerThread(TAG);
thread.start(); thread.start();
Handler handler = new Handler(thread.getLooper()); Handler handler = new Handler(thread.getLooper());
NsdService service = new NsdService(context, settings, handler, DaemonConnection::new); NsdService service = new NsdService(context, settings, handler,
DaemonConnection::new, CLEANUP_DELAY_MS);
service.mDaemonCallback.awaitConnection(); service.mDaemonCallback.awaitConnection();
return service; return service;
} }
@@ -681,12 +706,16 @@ public class NsdService extends INsdManager.Stub {
@VisibleForTesting @VisibleForTesting
public static class DaemonConnection { public static class DaemonConnection {
final NativeDaemonConnector mNativeConnector; final NativeDaemonConnector mNativeConnector;
boolean mIsStarted = false;
DaemonConnection(NativeCallbackReceiver callback) { DaemonConnection(NativeCallbackReceiver callback) {
mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null); mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
new Thread(mNativeConnector, MDNS_TAG).start(); new Thread(mNativeConnector, MDNS_TAG).start();
} }
/**
* Executes the specified cmd on the daemon.
*/
public boolean execute(Object... args) { public boolean execute(Object... args) {
if (DBG) { if (DBG) {
Slog.d(TAG, "mdnssd " + Arrays.toString(args)); Slog.d(TAG, "mdnssd " + Arrays.toString(args));
@@ -700,12 +729,26 @@ public class NsdService extends INsdManager.Stub {
return true; return true;
} }
public void start() { /**
* Starts the daemon if it is not already started.
*/
public void maybeStart() {
if (mIsStarted) {
return;
}
execute("start-service"); execute("start-service");
mIsStarted = true;
} }
public void stop() { /**
* Stops the daemon if it is started.
*/
public void maybeStop() {
if (!mIsStarted) {
return;
}
execute("stop-service"); execute("stop-service");
mIsStarted = false;
} }
} }
@@ -864,6 +907,7 @@ public class NsdService extends INsdManager.Stub {
} }
mClientIds.clear(); mClientIds.clear();
mClientRequests.clear(); mClientRequests.clear();
mNsdStateMachine.maybeScheduleStop();
} }
// mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id, // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,