diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 5a25cfc00c..ae8d010194 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -23,6 +23,9 @@ import static com.android.internal.util.Preconditions.checkStringNotEmpty; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; @@ -125,6 +128,24 @@ public final class NsdManager { private static final String TAG = NsdManager.class.getSimpleName(); private static final boolean DBG = false; + /** + * When enabled, apps targeting < Android 12 are considered legacy for + * the NSD native daemon. + * The platform will only keep the daemon running as long as there are + * any legacy apps connected. + * + * After Android 12, directly communicate with native daemon might not + * work since the native damon won't always stay alive. + * Use the NSD APIs from NsdManager as the replacement is recommended. + * An another alternative could be bundling your own mdns solutions instead of + * depending on the system mdns native daemon. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS = 191844585L; + /** * Broadcast intent action to indicate whether network service discovery is * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state @@ -202,6 +223,9 @@ public final class NsdManager { /** @hide */ public static final int DAEMON_CLEANUP = BASE + 21; + /** @hide */ + public static final int DAEMON_STARTUP = BASE + 22; + /** @hide */ public static final int ENABLE = BASE + 24; /** @hide */ @@ -232,6 +256,8 @@ public final class NsdManager { EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); + EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP"); + EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP"); EVENT_NAMES.put(ENABLE, "ENABLE"); EVENT_NAMES.put(DISABLE, "DISABLE"); EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT"); @@ -494,6 +520,12 @@ public final class NsdManager { } 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) { diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index a9f3a1b63b..462ed5c70d 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -82,6 +82,8 @@ public class NsdService extends INsdManager.Stub { private static final int INVALID_ID = 0; private int mUniqueId = 1; + // The count of the connected legacy clients. + private int mLegacyClientCount = 0; private class NsdStateMachine extends StateMachine { @@ -107,7 +109,9 @@ public class NsdService extends INsdManager.Stub { sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs); } private void maybeScheduleStop() { - if (!isAnyRequestActive()) { + // The native daemon should stay alive and can't be cleanup + // if any legacy client connected. + if (!isAnyRequestActive() && mLegacyClientCount == 0) { scheduleStop(); } } @@ -175,11 +179,11 @@ public class NsdService extends INsdManager.Stub { if (cInfo != null) { cInfo.expungeAllRequests(); mClients.remove(msg.replyTo); + if (cInfo.isLegacy()) { + mLegacyClientCount -= 1; + } } - //Last client - if (mClients.size() == 0) { - scheduleStop(); - } + maybeScheduleStop(); break; case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: AsyncChannel ac = new AsyncChannel(); @@ -208,6 +212,17 @@ public class NsdService extends INsdManager.Stub { case NsdManager.DAEMON_CLEANUP: mDaemon.maybeStop(); break; + // This event should be only sent by the legacy (target SDK < S) clients. + // Mark the sending client as legacy. + case NsdManager.DAEMON_STARTUP: + cInfo = mClients.get(msg.replyTo); + if (cInfo != null) { + cancelStop(); + cInfo.setLegacy(); + mLegacyClientCount += 1; + maybeStartDaemon(); + } + break; case NsdManager.NATIVE_DAEMON_EVENT: default: Slog.e(TAG, "Unhandled " + msg); @@ -863,6 +878,9 @@ public class NsdService extends INsdManager.Stub { /* A map from client id to the type of the request we had received */ private final SparseIntArray mClientRequests = new SparseIntArray(); + // The target SDK of this client < Build.VERSION_CODES.S + private boolean mIsLegacy = false; + private ClientInfo(AsyncChannel c, Messenger m) { mChannel = c; mMessenger = m; @@ -875,6 +893,7 @@ public class NsdService extends INsdManager.Stub { sb.append("mChannel ").append(mChannel).append("\n"); sb.append("mMessenger ").append(mMessenger).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n"); + sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); for(int i = 0; i< mClientIds.size(); i++) { int clientID = mClientIds.keyAt(i); sb.append("clientId ").append(clientID). @@ -884,6 +903,14 @@ public class NsdService extends INsdManager.Stub { return sb.toString(); } + private boolean isLegacy() { + return mIsLegacy; + } + + private void setLegacy() { + mIsLegacy = true; + } + // Remove any pending requests from the global map when we get rid of a client, // and send cancellations to the daemon. private void expungeAllRequests() {