Merge changes from topic "MDns_AIDL"
* changes: Use MDns aidl on NsdService Add MDnsManager
This commit is contained in:
@@ -20,7 +20,9 @@ import android.annotation.SystemApi;
|
|||||||
import android.app.SystemServiceRegistry;
|
import android.app.SystemServiceRegistry;
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.mdns.aidl.IMDns;
|
||||||
import android.net.nsd.INsdManager;
|
import android.net.nsd.INsdManager;
|
||||||
|
import android.net.nsd.MDnsManager;
|
||||||
import android.net.nsd.NsdManager;
|
import android.net.nsd.NsdManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,5 +80,14 @@ public final class ConnectivityFrameworkInitializerTiramisu {
|
|||||||
return new EthernetManager(context, service);
|
return new EthernetManager(context, service);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SystemServiceRegistry.registerStaticService(
|
||||||
|
MDnsManager.MDNS_SERVICE,
|
||||||
|
MDnsManager.class,
|
||||||
|
(serviceBinder) -> {
|
||||||
|
IMDns service = IMDns.Stub.asInterface(serviceBinder);
|
||||||
|
return new MDnsManager(service);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
200
framework-t/src/android/net/nsd/MDnsManager.java
Normal file
200
framework-t/src/android/net/nsd/MDnsManager.java
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.annotation.NonNull;
|
||||||
|
import android.net.mdns.aidl.DiscoveryInfo;
|
||||||
|
import android.net.mdns.aidl.GetAddressInfo;
|
||||||
|
import android.net.mdns.aidl.IMDns;
|
||||||
|
import android.net.mdns.aidl.IMDnsEventListener;
|
||||||
|
import android.net.mdns.aidl.RegistrationInfo;
|
||||||
|
import android.net.mdns.aidl.ResolutionInfo;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceSpecificException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager class for mdns service.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class MDnsManager {
|
||||||
|
private static final String TAG = MDnsManager.class.getSimpleName();
|
||||||
|
private final IMDns mMdns;
|
||||||
|
|
||||||
|
/** Service name for this. */
|
||||||
|
public static final String MDNS_SERVICE = "mdns";
|
||||||
|
|
||||||
|
private static final int NO_RESULT = -1;
|
||||||
|
private static final int NETID_UNSET = 0;
|
||||||
|
|
||||||
|
public MDnsManager(IMDns mdns) {
|
||||||
|
mMdns = mdns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the MDNSResponder daemon.
|
||||||
|
*/
|
||||||
|
public void startDaemon() {
|
||||||
|
try {
|
||||||
|
mMdns.startDaemon();
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Start mdns failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the MDNSResponder daemon.
|
||||||
|
*/
|
||||||
|
public void stopDaemon() {
|
||||||
|
try {
|
||||||
|
mMdns.stopDaemon();
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Stop mdns failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start registering a service.
|
||||||
|
*
|
||||||
|
* @param id The operation ID.
|
||||||
|
* @param serviceName The service name to be registered.
|
||||||
|
* @param registrationType The service type to be registered.
|
||||||
|
* @param port The port on which the service accepts connections.
|
||||||
|
* @param txtRecord The txt record. Refer to {@code NsdServiceInfo#setTxtRecords} for details.
|
||||||
|
* @param interfaceIdx The interface index on which to register the service.
|
||||||
|
* @return {@code true} if registration is successful, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean registerService(int id, @NonNull String serviceName,
|
||||||
|
@NonNull String registrationType, int port, @NonNull byte[] txtRecord,
|
||||||
|
int interfaceIdx) {
|
||||||
|
final RegistrationInfo info = new RegistrationInfo(id, NO_RESULT, serviceName,
|
||||||
|
registrationType, port, txtRecord, interfaceIdx);
|
||||||
|
try {
|
||||||
|
mMdns.registerService(info);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Register service failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start discovering services.
|
||||||
|
*
|
||||||
|
* @param id The operation ID.
|
||||||
|
* @param registrationType The service type to be discovered.
|
||||||
|
* @param interfaceIdx The interface index on which to discover for services.
|
||||||
|
* @return {@code true} if discovery is started successfully, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean discover(int id, @NonNull String registrationType, int interfaceIdx) {
|
||||||
|
final DiscoveryInfo info = new DiscoveryInfo(id, NO_RESULT, "" /* serviceName */,
|
||||||
|
registrationType, "" /* domainName */, interfaceIdx, NETID_UNSET);
|
||||||
|
try {
|
||||||
|
mMdns.discover(info);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Discover service failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start resolving the target service.
|
||||||
|
*
|
||||||
|
* @param id The operation ID.
|
||||||
|
* @param serviceName The service name to be resolved.
|
||||||
|
* @param registrationType The service type to be resolved.
|
||||||
|
* @param domain The service domain to be resolved.
|
||||||
|
* @param interfaceIdx The interface index on which to resolve the service.
|
||||||
|
* @return {@code true} if resolution is started successfully, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean resolve(int id, @NonNull String serviceName, @NonNull String registrationType,
|
||||||
|
@NonNull String domain, int interfaceIdx) {
|
||||||
|
final ResolutionInfo info = new ResolutionInfo(id, NO_RESULT, serviceName,
|
||||||
|
registrationType, domain, "" /* serviceFullName */, "" /* hostname */, 0 /* port */,
|
||||||
|
new byte[0] /* txtRecord */, interfaceIdx);
|
||||||
|
try {
|
||||||
|
mMdns.resolve(info);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Resolve service failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start getting the target service address.
|
||||||
|
*
|
||||||
|
* @param id The operation ID.
|
||||||
|
* @param hostname The fully qualified domain name of the host to be queried for.
|
||||||
|
* @param interfaceIdx The interface index on which to issue the query.
|
||||||
|
* @return {@code true} if getting address is started successful, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean getServiceAddress(int id, @NonNull String hostname, int interfaceIdx) {
|
||||||
|
final GetAddressInfo info = new GetAddressInfo(id, NO_RESULT, hostname,
|
||||||
|
"" /* address */, interfaceIdx, NETID_UNSET);
|
||||||
|
try {
|
||||||
|
mMdns.getServiceAddress(info);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Get service address failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop an operation which was requested before.
|
||||||
|
*
|
||||||
|
* @param id the operation id to be stopped.
|
||||||
|
* @return {@code true} if operation is stopped successfully, else {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean stopOperation(int id) {
|
||||||
|
try {
|
||||||
|
mMdns.stopOperation(id);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Stop operation failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an event listener.
|
||||||
|
*
|
||||||
|
* @param listener The listener to be registered.
|
||||||
|
*/
|
||||||
|
public void registerEventListener(@NonNull IMDnsEventListener listener) {
|
||||||
|
try {
|
||||||
|
mMdns.registerEventListener(listener);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Register listener failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an event listener.
|
||||||
|
*
|
||||||
|
* @param listener The listener to be unregistered.
|
||||||
|
*/
|
||||||
|
public void unregisterEventListener(@NonNull IMDnsEventListener listener) {
|
||||||
|
try {
|
||||||
|
mMdns.unregisterEventListener(listener);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
Log.e(TAG, "Unregister listener failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -235,7 +235,7 @@ public final class NsdManager {
|
|||||||
public static final int DISABLE = 21;
|
public static final int DISABLE = 21;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int NATIVE_DAEMON_EVENT = 22;
|
public static final int MDNS_SERVICE_EVENT = 22;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int REGISTER_CLIENT = 23;
|
public static final int REGISTER_CLIENT = 23;
|
||||||
@@ -268,7 +268,7 @@ public final class NsdManager {
|
|||||||
EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP");
|
EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP");
|
||||||
EVENT_NAMES.put(ENABLE, "ENABLE");
|
EVENT_NAMES.put(ENABLE, "ENABLE");
|
||||||
EVENT_NAMES.put(DISABLE, "DISABLE");
|
EVENT_NAMES.put(DISABLE, "DISABLE");
|
||||||
EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT");
|
EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@@ -106,13 +105,11 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Unpack txt information from a base-64 encoded byte array.
|
* Unpack txt information from a base-64 encoded byte array.
|
||||||
*
|
*
|
||||||
* @param rawRecords The raw base64 encoded records string read from netd.
|
* @param txtRecordsRawBytes The raw base64 encoded byte array.
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public void setTxtRecords(@NonNull String rawRecords) {
|
public void setTxtRecords(@NonNull byte[] txtRecordsRawBytes) {
|
||||||
byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT);
|
|
||||||
|
|
||||||
// There can be multiple TXT records after each other. Each record has to following format:
|
// There can be multiple TXT records after each other. Each record has to following format:
|
||||||
//
|
//
|
||||||
// byte type required meaning
|
// byte type required meaning
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ java_defaults {
|
|||||||
"net-utils-device-common",
|
"net-utils-device-common",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
|
"mdns_aidl_interface-lateststable-java",
|
||||||
"modules-utils-backgroundthread",
|
"modules-utils-backgroundthread",
|
||||||
"modules-utils-build",
|
"modules-utils-build",
|
||||||
"modules-utils-preconditions",
|
"modules-utils-preconditions",
|
||||||
|
|||||||
@@ -16,15 +16,24 @@
|
|||||||
|
|
||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
|
import static android.net.ConnectivityManager.NETID_UNSET;
|
||||||
|
import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
|
import android.net.mdns.aidl.DiscoveryInfo;
|
||||||
|
import android.net.mdns.aidl.GetAddressInfo;
|
||||||
|
import android.net.mdns.aidl.IMDnsEventListener;
|
||||||
|
import android.net.mdns.aidl.RegistrationInfo;
|
||||||
|
import android.net.mdns.aidl.ResolutionInfo;
|
||||||
import android.net.nsd.INsdManager;
|
import android.net.nsd.INsdManager;
|
||||||
import android.net.nsd.INsdManagerCallback;
|
import android.net.nsd.INsdManagerCallback;
|
||||||
import android.net.nsd.INsdServiceConnector;
|
import android.net.nsd.INsdServiceConnector;
|
||||||
|
import android.net.nsd.MDnsManager;
|
||||||
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;
|
||||||
@@ -33,7 +42,6 @@ import android.os.IBinder;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -42,7 +50,6 @@ import android.util.SparseIntArray;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.State;
|
import com.android.internal.util.State;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
import com.android.net.module.util.DnsSdTxtRecord;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@@ -50,9 +57,7 @@ import java.net.InetAddress;
|
|||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network Service Discovery Service handles remote service discovery operation requests by
|
* Network Service Discovery Service handles remote service discovery operation requests by
|
||||||
@@ -64,14 +69,18 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
private static final String TAG = "NsdService";
|
private static final String TAG = "NsdService";
|
||||||
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 = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
private static final long CLEANUP_DELAY_MS = 10000;
|
private static final long CLEANUP_DELAY_MS = 10000;
|
||||||
private static final int IFACE_IDX_ANY = 0;
|
private static final int IFACE_IDX_ANY = 0;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final NsdStateMachine mNsdStateMachine;
|
private final NsdStateMachine mNsdStateMachine;
|
||||||
private final DaemonConnection mDaemon;
|
private final MDnsManager mMDnsManager;
|
||||||
private final NativeCallbackReceiver mDaemonCallback;
|
private final MDnsEventCallback mMDnsEventCallback;
|
||||||
|
// WARNING : Accessing this value in any thread is not safe, it must only be changed in the
|
||||||
|
// state machine thread. If change this outside state machine, it will need to introduce
|
||||||
|
// synchronization.
|
||||||
|
private boolean mIsDaemonStarted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clients receiving asynchronous messages
|
* Clients receiving asynchronous messages
|
||||||
@@ -100,10 +109,26 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void maybeStartDaemon() {
|
private void maybeStartDaemon() {
|
||||||
mDaemon.maybeStart();
|
if (mIsDaemonStarted) {
|
||||||
|
if (DBG) Log.d(TAG, "Daemon is already started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mMDnsManager.registerEventListener(mMDnsEventCallback);
|
||||||
|
mMDnsManager.startDaemon();
|
||||||
|
mIsDaemonStarted = true;
|
||||||
maybeScheduleStop();
|
maybeScheduleStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeStopDaemon() {
|
||||||
|
if (!mIsDaemonStarted) {
|
||||||
|
if (DBG) Log.d(TAG, "Daemon has not been started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mMDnsManager.unregisterEventListener(mMDnsEventCallback);
|
||||||
|
mMDnsManager.stopDaemon();
|
||||||
|
mIsDaemonStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAnyRequestActive() {
|
private boolean isAnyRequestActive() {
|
||||||
return mIdToClientInfoMap.size() != 0;
|
return mIdToClientInfoMap.size() != 0;
|
||||||
}
|
}
|
||||||
@@ -198,7 +223,7 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NsdManager.DAEMON_CLEANUP:
|
case NsdManager.DAEMON_CLEANUP:
|
||||||
mDaemon.maybeStop();
|
maybeStopDaemon();
|
||||||
break;
|
break;
|
||||||
// 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.
|
||||||
@@ -211,7 +236,6 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
maybeStartDaemon();
|
maybeStartDaemon();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NsdManager.NATIVE_DAEMON_EVENT:
|
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Unhandled " + msg);
|
Log.e(TAG, "Unhandled " + msg);
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
@@ -397,9 +421,8 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NsdManager.NATIVE_DAEMON_EVENT:
|
case MDNS_SERVICE_EVENT:
|
||||||
NativeEvent event = (NativeEvent) msg.obj;
|
if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
|
||||||
if (!handleNativeEvent(event.code, event.raw, event.cooked)) {
|
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -409,13 +432,11 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return HANDLED;
|
return HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleNativeEvent(int code, String raw, String[] cooked) {
|
private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
|
||||||
NsdServiceInfo servInfo;
|
NsdServiceInfo servInfo;
|
||||||
int id = Integer.parseInt(cooked[1]);
|
|
||||||
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
|
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
|
||||||
if (clientInfo == null) {
|
if (clientInfo == null) {
|
||||||
String name = NativeResponseCode.nameOf(code);
|
Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
|
||||||
Log.e(TAG, String.format("id %d for %s has no client mapping", id, name));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,27 +446,20 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
// This can happen because of race conditions. For example,
|
// This can happen because of race conditions. For example,
|
||||||
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
|
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
|
||||||
// and we may get in this situation.
|
// and we may get in this situation.
|
||||||
String name = NativeResponseCode.nameOf(code);
|
Log.d(TAG, String.format("%d for listener id %d that is no longer active",
|
||||||
Log.d(TAG, String.format(
|
code, id));
|
||||||
"Notification %s for listener id %d that is no longer active",
|
|
||||||
name, id));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
String name = NativeResponseCode.nameOf(code);
|
Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
|
||||||
Log.d(TAG, String.format("Native daemon message %s: %s", name, raw));
|
|
||||||
}
|
}
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case NativeResponseCode.SERVICE_FOUND:
|
case IMDnsEventListener.SERVICE_FOUND: {
|
||||||
/* NNN uniqueId serviceName regType domain interfaceIdx netId */
|
final DiscoveryInfo info = (DiscoveryInfo) obj;
|
||||||
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
|
final String name = info.serviceName;
|
||||||
final int foundNetId;
|
final String type = info.registrationType;
|
||||||
try {
|
servInfo = new NsdServiceInfo(name, type);
|
||||||
foundNetId = Integer.parseInt(cooked[6]);
|
final int foundNetId = info.netId;
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (foundNetId == 0L) {
|
if (foundNetId == 0L) {
|
||||||
// Ignore services that do not have a Network: they are not usable
|
// Ignore services that do not have a Network: they are not usable
|
||||||
// by apps, as they would need privileged permissions to use
|
// by apps, as they would need privileged permissions to use
|
||||||
@@ -455,74 +469,65 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
servInfo.setNetwork(new Network(foundNetId));
|
servInfo.setNetwork(new Network(foundNetId));
|
||||||
clientInfo.onServiceFound(clientId, servInfo);
|
clientInfo.onServiceFound(clientId, servInfo);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_LOST:
|
|
||||||
/* NNN uniqueId serviceName regType domain interfaceIdx netId */
|
|
||||||
final int lostNetId;
|
|
||||||
try {
|
|
||||||
lostNetId = Integer.parseInt(cooked[6]);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
|
case IMDnsEventListener.SERVICE_LOST: {
|
||||||
|
final DiscoveryInfo info = (DiscoveryInfo) obj;
|
||||||
|
final String name = info.serviceName;
|
||||||
|
final String type = info.registrationType;
|
||||||
|
final int lostNetId = info.netId;
|
||||||
|
servInfo = new NsdServiceInfo(name, type);
|
||||||
// The network could be null if it was torn down when the service is lost
|
// The network could be null if it was torn down when the service is lost
|
||||||
// TODO: avoid returning null in that case, possibly by remembering found
|
// TODO: avoid returning null in that case, possibly by remembering found
|
||||||
// services on the same interface index and their network at the time
|
// services on the same interface index and their network at the time
|
||||||
servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
|
servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
|
||||||
clientInfo.onServiceLost(clientId, servInfo);
|
clientInfo.onServiceLost(clientId, servInfo);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
|
}
|
||||||
/* NNN uniqueId errorCode */
|
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
|
||||||
clientInfo.onDiscoverServicesFailed(
|
clientInfo.onDiscoverServicesFailed(
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_REGISTERED:
|
case IMDnsEventListener.SERVICE_REGISTERED: {
|
||||||
/* NNN regId serviceName regType */
|
final RegistrationInfo info = (RegistrationInfo) obj;
|
||||||
servInfo = new NsdServiceInfo(cooked[2], null);
|
final String name = info.serviceName;
|
||||||
|
servInfo = new NsdServiceInfo(name, null /* serviceType */);
|
||||||
clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
|
clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
|
}
|
||||||
/* NNN regId errorCode */
|
case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
|
||||||
clientInfo.onRegisterServiceFailed(
|
clientInfo.onRegisterServiceFailed(
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_UPDATED:
|
case IMDnsEventListener.SERVICE_RESOLVED: {
|
||||||
/* NNN regId */
|
final ResolutionInfo info = (ResolutionInfo) obj;
|
||||||
break;
|
|
||||||
case NativeResponseCode.SERVICE_UPDATE_FAILED:
|
|
||||||
/* NNN regId errorCode */
|
|
||||||
break;
|
|
||||||
case NativeResponseCode.SERVICE_RESOLVED:
|
|
||||||
/* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
|
final String fullName = info.serviceFullName;
|
||||||
if (cooked[2].charAt(index) == '\\') {
|
while (index < fullName.length() && fullName.charAt(index) != '.') {
|
||||||
|
if (fullName.charAt(index) == '\\') {
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
if (index >= cooked[2].length()) {
|
if (index >= fullName.length()) {
|
||||||
Log.e(TAG, "Invalid service found " + raw);
|
Log.e(TAG, "Invalid service found " + fullName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = cooked[2].substring(0, index);
|
String name = fullName.substring(0, index);
|
||||||
String rest = cooked[2].substring(index);
|
String rest = fullName.substring(index);
|
||||||
String type = rest.replace(".local.", "");
|
String type = rest.replace(".local.", "");
|
||||||
|
|
||||||
name = unescape(name);
|
|
||||||
|
|
||||||
clientInfo.mResolvedService.setServiceName(name);
|
clientInfo.mResolvedService.setServiceName(name);
|
||||||
clientInfo.mResolvedService.setServiceType(type);
|
clientInfo.mResolvedService.setServiceType(type);
|
||||||
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
|
clientInfo.mResolvedService.setPort(info.port);
|
||||||
clientInfo.mResolvedService.setTxtRecords(cooked[6]);
|
clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
|
||||||
// Network will be added after SERVICE_GET_ADDR_SUCCESS
|
// Network will be added after SERVICE_GET_ADDR_SUCCESS
|
||||||
|
|
||||||
stopResolveService(id);
|
stopResolveService(id);
|
||||||
removeRequestMap(clientId, id, clientInfo);
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
|
|
||||||
int id2 = getUniqueId();
|
final int id2 = getUniqueId();
|
||||||
if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) {
|
if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
|
||||||
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
|
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
|
||||||
} else {
|
} else {
|
||||||
clientInfo.onResolveServiceFailed(
|
clientInfo.onResolveServiceFailed(
|
||||||
@@ -530,7 +535,8 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientInfo.mResolvedService = null;
|
clientInfo.mResolvedService = null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
|
}
|
||||||
|
case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
|
||||||
/* NNN resolveId errorCode */
|
/* NNN resolveId errorCode */
|
||||||
stopResolveService(id);
|
stopResolveService(id);
|
||||||
removeRequestMap(clientId, id, clientInfo);
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
@@ -538,7 +544,7 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientInfo.onResolveServiceFailed(
|
clientInfo.onResolveServiceFailed(
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
|
case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
|
||||||
/* NNN resolveId errorCode */
|
/* NNN resolveId errorCode */
|
||||||
stopGetAddrInfo(id);
|
stopGetAddrInfo(id);
|
||||||
removeRequestMap(clientId, id, clientInfo);
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
@@ -546,19 +552,15 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientInfo.onResolveServiceFailed(
|
clientInfo.onResolveServiceFailed(
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
break;
|
break;
|
||||||
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
|
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
|
||||||
/* NNN resolveId hostname ttl addr interfaceIdx netId */
|
/* NNN resolveId hostname ttl addr interfaceIdx netId */
|
||||||
Network network = null;
|
final GetAddressInfo info = (GetAddressInfo) obj;
|
||||||
try {
|
final String address = info.address;
|
||||||
final int netId = Integer.parseInt(cooked[6]);
|
final int netId = info.netId;
|
||||||
network = netId == 0L ? null : new Network(netId);
|
final Network network = netId == NETID_UNSET ? null : new Network(netId);
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e);
|
|
||||||
}
|
|
||||||
|
|
||||||
InetAddress serviceHost = null;
|
InetAddress serviceHost = null;
|
||||||
try {
|
try {
|
||||||
serviceHost = InetAddress.getByName(cooked[4]);
|
serviceHost = InetAddress.getByName(address);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
|
Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
|
||||||
}
|
}
|
||||||
@@ -579,6 +581,7 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
removeRequestMap(clientId, id, clientInfo);
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
clientInfo.mResolvedService = null;
|
clientInfo.mResolvedService = null;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -587,50 +590,66 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String unescape(String s) {
|
|
||||||
StringBuilder sb = new StringBuilder(s.length());
|
|
||||||
for (int i = 0; i < s.length(); ++i) {
|
|
||||||
char c = s.charAt(i);
|
|
||||||
if (c == '\\') {
|
|
||||||
if (++i >= s.length()) {
|
|
||||||
Log.e(TAG, "Unexpected end of escape sequence in: " + s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = s.charAt(i);
|
|
||||||
if (c != '.' && c != '\\') {
|
|
||||||
if (i + 2 >= s.length()) {
|
|
||||||
Log.e(TAG, "Unexpected end of escape sequence in: " + s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
NsdService(Context ctx, Handler handler, DaemonConnectionSupplier fn, long cleanupDelayMs) {
|
NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
|
||||||
mCleanupDelayMs = cleanupDelayMs;
|
mCleanupDelayMs = cleanupDelayMs;
|
||||||
mContext = ctx;
|
mContext = ctx;
|
||||||
mNsdStateMachine = new NsdStateMachine(TAG, handler);
|
mNsdStateMachine = new NsdStateMachine(TAG, handler);
|
||||||
mNsdStateMachine.start();
|
mNsdStateMachine.start();
|
||||||
mDaemonCallback = new NativeCallbackReceiver();
|
mMDnsManager = ctx.getSystemService(MDnsManager.class);
|
||||||
mDaemon = fn.get(mDaemonCallback);
|
mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NsdService create(Context context) throws InterruptedException {
|
public static NsdService create(Context context) throws InterruptedException {
|
||||||
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 =
|
NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
|
||||||
new NsdService(context, handler, DaemonConnection::new, CLEANUP_DELAY_MS);
|
|
||||||
service.mDaemonCallback.awaitConnection();
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MDnsEventCallback extends IMDnsEventListener.Stub {
|
||||||
|
private final StateMachine mStateMachine;
|
||||||
|
|
||||||
|
MDnsEventCallback(StateMachine sm) {
|
||||||
|
mStateMachine = sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceRegistrationStatus(final RegistrationInfo status) {
|
||||||
|
mStateMachine.sendMessage(
|
||||||
|
MDNS_SERVICE_EVENT, status.result, status.id, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
|
||||||
|
mStateMachine.sendMessage(
|
||||||
|
MDNS_SERVICE_EVENT, status.result, status.id, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceResolutionStatus(final ResolutionInfo status) {
|
||||||
|
mStateMachine.sendMessage(
|
||||||
|
MDNS_SERVICE_EVENT, status.result, status.id, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGettingServiceAddressStatus(final GetAddressInfo status) {
|
||||||
|
mStateMachine.sendMessage(
|
||||||
|
MDNS_SERVICE_EVENT, status.result, status.id, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInterfaceVersion() throws RemoteException {
|
||||||
|
return this.VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getInterfaceHash() throws RemoteException {
|
||||||
|
return this.HASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public INsdServiceConnector connect(INsdManagerCallback cb) {
|
public INsdServiceConnector connect(INsdManagerCallback cb) {
|
||||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
|
||||||
@@ -711,140 +730,6 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return mUniqueId;
|
return mUniqueId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* These should be in sync with system/netd/server/ResponseCode.h */
|
|
||||||
static final class NativeResponseCode {
|
|
||||||
public static final int SERVICE_DISCOVERY_FAILED = 602;
|
|
||||||
public static final int SERVICE_FOUND = 603;
|
|
||||||
public static final int SERVICE_LOST = 604;
|
|
||||||
|
|
||||||
public static final int SERVICE_REGISTRATION_FAILED = 605;
|
|
||||||
public static final int SERVICE_REGISTERED = 606;
|
|
||||||
|
|
||||||
public static final int SERVICE_RESOLUTION_FAILED = 607;
|
|
||||||
public static final int SERVICE_RESOLVED = 608;
|
|
||||||
|
|
||||||
public static final int SERVICE_UPDATED = 609;
|
|
||||||
public static final int SERVICE_UPDATE_FAILED = 610;
|
|
||||||
|
|
||||||
public static final int SERVICE_GET_ADDR_FAILED = 611;
|
|
||||||
public static final int SERVICE_GET_ADDR_SUCCESS = 612;
|
|
||||||
|
|
||||||
private static final SparseArray<String> CODE_NAMES = new SparseArray<>();
|
|
||||||
static {
|
|
||||||
CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED");
|
|
||||||
CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
|
|
||||||
CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
|
|
||||||
CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED");
|
|
||||||
CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED");
|
|
||||||
CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED");
|
|
||||||
CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED");
|
|
||||||
CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
|
|
||||||
CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED");
|
|
||||||
CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED");
|
|
||||||
CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS");
|
|
||||||
}
|
|
||||||
|
|
||||||
static String nameOf(int code) {
|
|
||||||
String name = CODE_NAMES.get(code);
|
|
||||||
if (name == null) {
|
|
||||||
return Integer.toString(code);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NativeEvent {
|
|
||||||
final int code;
|
|
||||||
final String raw;
|
|
||||||
final String[] cooked;
|
|
||||||
|
|
||||||
NativeEvent(int code, String raw, String[] cooked) {
|
|
||||||
this.code = code;
|
|
||||||
this.raw = raw;
|
|
||||||
this.cooked = cooked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
|
|
||||||
private final CountDownLatch connected = new CountDownLatch(1);
|
|
||||||
|
|
||||||
public void awaitConnection() throws InterruptedException {
|
|
||||||
connected.await();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDaemonConnected() {
|
|
||||||
connected.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCheckHoldWakeLock(int code) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onEvent(int code, String raw, String[] cooked) {
|
|
||||||
// TODO: NDC translates a message to a callback, we could enhance NDC to
|
|
||||||
// directly interact with a state machine through messages
|
|
||||||
NativeEvent event = new NativeEvent(code, raw, cooked);
|
|
||||||
mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DaemonConnectionSupplier {
|
|
||||||
DaemonConnection get(NativeCallbackReceiver callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static class DaemonConnection {
|
|
||||||
final NativeDaemonConnector mNativeConnector;
|
|
||||||
boolean mIsStarted = false;
|
|
||||||
|
|
||||||
DaemonConnection(NativeCallbackReceiver callback) {
|
|
||||||
mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
|
|
||||||
new Thread(mNativeConnector, MDNS_TAG).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the specified cmd on the daemon.
|
|
||||||
*/
|
|
||||||
public boolean execute(Object... args) {
|
|
||||||
if (DBG) {
|
|
||||||
Log.d(TAG, "mdnssd " + Arrays.toString(args));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mNativeConnector.execute("mdnssd", args);
|
|
||||||
} catch (NativeDaemonConnectorException e) {
|
|
||||||
Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the daemon if it is not already started.
|
|
||||||
*/
|
|
||||||
public void maybeStart() {
|
|
||||||
if (mIsStarted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
execute("start-service");
|
|
||||||
mIsStarted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the daemon if it is started.
|
|
||||||
*/
|
|
||||||
public void maybeStop() {
|
|
||||||
if (!mIsStarted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
execute("stop-service");
|
|
||||||
mIsStarted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean registerService(int regId, NsdServiceInfo service) {
|
private boolean registerService(int regId, NsdServiceInfo service) {
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(TAG, "registerService: " + regId + " " + service);
|
Log.d(TAG, "registerService: " + regId + " " + service);
|
||||||
@@ -853,34 +738,26 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
String type = service.getServiceType();
|
String type = service.getServiceType();
|
||||||
int port = service.getPort();
|
int port = service.getPort();
|
||||||
byte[] textRecord = service.getTxtRecord();
|
byte[] textRecord = service.getTxtRecord();
|
||||||
String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
|
return mMDnsManager.registerService(regId, name, type, port, textRecord, IFACE_IDX_ANY);
|
||||||
return mDaemon.execute("register", regId, name, type, port, record);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean unregisterService(int regId) {
|
private boolean unregisterService(int regId) {
|
||||||
return mDaemon.execute("stop-register", regId);
|
return mMDnsManager.stopOperation(regId);
|
||||||
}
|
|
||||||
|
|
||||||
private boolean updateService(int regId, DnsSdTxtRecord t) {
|
|
||||||
if (t == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mDaemon.execute("update", regId, t.size(), t.getRawData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
|
private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
|
||||||
final Network network = serviceInfo.getNetwork();
|
final Network network = serviceInfo.getNetwork();
|
||||||
|
final String type = serviceInfo.getServiceType();
|
||||||
final int discoverInterface = getNetworkInterfaceIndex(network);
|
final int discoverInterface = getNetworkInterfaceIndex(network);
|
||||||
if (network != null && discoverInterface == IFACE_IDX_ANY) {
|
if (network != null && discoverInterface == IFACE_IDX_ANY) {
|
||||||
Log.e(TAG, "Interface to discover service on not found");
|
Log.e(TAG, "Interface to discover service on not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(),
|
return mMDnsManager.discover(discoveryId, type, discoverInterface);
|
||||||
discoverInterface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stopServiceDiscovery(int discoveryId) {
|
private boolean stopServiceDiscovery(int discoveryId) {
|
||||||
return mDaemon.execute("stop-discover", discoveryId);
|
return mMDnsManager.stopOperation(discoveryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean resolveService(int resolveId, NsdServiceInfo service) {
|
private boolean resolveService(int resolveId, NsdServiceInfo service) {
|
||||||
@@ -892,7 +769,7 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
Log.e(TAG, "Interface to resolve service on not found");
|
Log.e(TAG, "Interface to resolve service on not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface);
|
return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -933,16 +810,15 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean stopResolveService(int resolveId) {
|
private boolean stopResolveService(int resolveId) {
|
||||||
return mDaemon.execute("stop-resolve", resolveId);
|
return mMDnsManager.stopOperation(resolveId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) {
|
private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
|
||||||
// interfaceIdx is always obtained (as string) from the service resolved callback
|
return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
|
||||||
return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stopGetAddrInfo(int resolveId) {
|
private boolean stopGetAddrInfo(int resolveId) {
|
||||||
return mDaemon.execute("stop-getaddrinfo", resolveId);
|
return mMDnsManager.stopOperation(resolveId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -108,5 +108,8 @@ rule com.android.networkstack.apishim.** com.android.connectivity.@0
|
|||||||
# From filegroup framework-connectivity-protos
|
# From filegroup framework-connectivity-protos
|
||||||
rule android.service.*Proto com.android.connectivity.@0
|
rule android.service.*Proto com.android.connectivity.@0
|
||||||
|
|
||||||
|
# From mdns-aidl-interface
|
||||||
|
rule android.net.mdns.aidl.** android.net.connectivity.@0
|
||||||
|
|
||||||
# Remaining are connectivity sources in com.android.server and com.android.server.connectivity:
|
# Remaining are connectivity sources in com.android.server and com.android.server.connectivity:
|
||||||
# TODO: move to a subpackage of com.android.connectivity (such as com.android.connectivity.server)
|
# TODO: move to a subpackage of com.android.connectivity (such as com.android.connectivity.server)
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ package com.android.server;
|
|||||||
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
|
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
|
||||||
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
|
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
@@ -36,6 +38,7 @@ import android.content.ContentResolver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.nsd.INsdManagerCallback;
|
import android.net.nsd.INsdManagerCallback;
|
||||||
import android.net.nsd.INsdServiceConnector;
|
import android.net.nsd.INsdServiceConnector;
|
||||||
|
import android.net.nsd.MDnsManager;
|
||||||
import android.net.nsd.NsdManager;
|
import android.net.nsd.NsdManager;
|
||||||
import android.net.nsd.NsdServiceInfo;
|
import android.net.nsd.NsdServiceInfo;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -49,9 +52,6 @@ import android.os.Message;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.server.NsdService.DaemonConnection;
|
|
||||||
import com.android.server.NsdService.DaemonConnectionSupplier;
|
|
||||||
import com.android.server.NsdService.NativeCallbackReceiver;
|
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRunner;
|
import com.android.testutils.DevSdkIgnoreRunner;
|
||||||
import com.android.testutils.HandlerUtils;
|
import com.android.testutils.HandlerUtils;
|
||||||
@@ -63,10 +63,8 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.TestRule;
|
import org.junit.rules.TestRule;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.AdditionalAnswers;
|
import org.mockito.AdditionalAnswers;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.Spy;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@@ -92,8 +90,7 @@ public class NsdServiceTest {
|
|||||||
public TestRule compatChangeRule = new PlatformCompatChangeRule();
|
public TestRule compatChangeRule = new PlatformCompatChangeRule();
|
||||||
@Mock Context mContext;
|
@Mock Context mContext;
|
||||||
@Mock ContentResolver mResolver;
|
@Mock ContentResolver mResolver;
|
||||||
NativeCallbackReceiver mDaemonCallback;
|
@Mock MDnsManager mMockMDnsM;
|
||||||
@Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
|
|
||||||
HandlerThread mThread;
|
HandlerThread mThread;
|
||||||
TestHandler mHandler;
|
TestHandler mHandler;
|
||||||
|
|
||||||
@@ -112,9 +109,17 @@ public class NsdServiceTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mThread = new HandlerThread("mock-service-handler");
|
mThread = new HandlerThread("mock-service-handler");
|
||||||
mThread.start();
|
mThread.start();
|
||||||
doReturn(true).when(mDaemon).execute(any());
|
|
||||||
mHandler = new TestHandler(mThread.getLooper());
|
mHandler = new TestHandler(mThread.getLooper());
|
||||||
when(mContext.getContentResolver()).thenReturn(mResolver);
|
when(mContext.getContentResolver()).thenReturn(mResolver);
|
||||||
|
doReturn(MDnsManager.MDNS_SERVICE).when(mContext)
|
||||||
|
.getSystemServiceName(MDnsManager.class);
|
||||||
|
doReturn(mMockMDnsM).when(mContext).getSystemService(MDnsManager.MDNS_SERVICE);
|
||||||
|
doReturn(true).when(mMockMDnsM).registerService(
|
||||||
|
anyInt(), anyString(), anyString(), anyInt(), any(), anyInt());
|
||||||
|
doReturn(true).when(mMockMDnsM).stopOperation(anyInt());
|
||||||
|
doReturn(true).when(mMockMDnsM).discover(anyInt(), anyString(), anyInt());
|
||||||
|
doReturn(true).when(mMockMDnsM).resolve(
|
||||||
|
anyInt(), anyString(), anyString(), anyString(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -135,24 +140,25 @@ public class NsdServiceTest {
|
|||||||
waitForIdle();
|
waitForIdle();
|
||||||
final INsdManagerCallback cb1 = getCallback();
|
final INsdManagerCallback cb1 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
|
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
verify(mMockMDnsM, times(1)).registerEventListener(any());
|
||||||
verifyDaemonCommands("start-service");
|
verify(mMockMDnsM, times(1)).startDaemon();
|
||||||
|
|
||||||
connectClient(service);
|
connectClient(service);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
final INsdManagerCallback cb2 = getCallback();
|
final INsdManagerCallback cb2 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
|
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
// Daemon has been started, it should not try to start it again.
|
||||||
|
verify(mMockMDnsM, times(1)).registerEventListener(any());
|
||||||
|
verify(mMockMDnsM, times(1)).startDaemon();
|
||||||
|
|
||||||
deathRecipient1.binderDied();
|
deathRecipient1.binderDied();
|
||||||
// Still 1 client remains, daemon shouldn't be stopped.
|
// Still 1 client remains, daemon shouldn't be stopped.
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, never()).maybeStop();
|
verify(mMockMDnsM, never()).stopDaemon();
|
||||||
|
|
||||||
deathRecipient2.binderDied();
|
deathRecipient2.binderDied();
|
||||||
// All clients are disconnected, the daemon should be stopped.
|
// All clients are disconnected, the daemon should be stopped.
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
||||||
verifyDaemonCommands("stop-service");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -160,28 +166,30 @@ public class NsdServiceTest {
|
|||||||
public void testNoDaemonStartedWhenClientsConnect() throws Exception {
|
public void testNoDaemonStartedWhenClientsConnect() throws Exception {
|
||||||
final NsdService service = makeService();
|
final NsdService service = makeService();
|
||||||
|
|
||||||
// Creating an NsdManager will not cause any cmds executed, which means
|
// Creating an NsdManager will not cause daemon startup.
|
||||||
// no daemon is started.
|
|
||||||
connectClient(service);
|
connectClient(service);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, never()).execute(any());
|
verify(mMockMDnsM, never()).registerEventListener(any());
|
||||||
|
verify(mMockMDnsM, never()).startDaemon();
|
||||||
final INsdManagerCallback cb1 = getCallback();
|
final INsdManagerCallback cb1 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
|
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
|
||||||
|
|
||||||
// Creating another NsdManager will not cause any cmds executed.
|
// Creating another NsdManager will not cause daemon startup either.
|
||||||
connectClient(service);
|
connectClient(service);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, never()).execute(any());
|
verify(mMockMDnsM, never()).registerEventListener(any());
|
||||||
|
verify(mMockMDnsM, never()).startDaemon();
|
||||||
final INsdManagerCallback cb2 = getCallback();
|
final INsdManagerCallback cb2 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
|
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
|
||||||
|
|
||||||
// If there is no active request, try to clean up the daemon
|
// If there is no active request, try to clean up the daemon but should not do it because
|
||||||
// every time the client disconnects.
|
// daemon has not been started.
|
||||||
deathRecipient1.binderDied();
|
deathRecipient1.binderDied();
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verify(mMockMDnsM, never()).unregisterEventListener(any());
|
||||||
reset(mDaemon);
|
verify(mMockMDnsM, never()).stopDaemon();
|
||||||
deathRecipient2.binderDied();
|
deathRecipient2.binderDied();
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verify(mMockMDnsM, never()).unregisterEventListener(any());
|
||||||
|
verify(mMockMDnsM, never()).stopDaemon();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
|
private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
|
||||||
@@ -200,8 +208,8 @@ public class NsdServiceTest {
|
|||||||
waitForIdle();
|
waitForIdle();
|
||||||
final INsdManagerCallback cb1 = getCallback();
|
final INsdManagerCallback cb1 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
|
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
|
||||||
verify(mDaemon, never()).maybeStart();
|
verify(mMockMDnsM, never()).registerEventListener(any());
|
||||||
verify(mDaemon, never()).execute(any());
|
verify(mMockMDnsM, never()).startDaemon();
|
||||||
|
|
||||||
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
|
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
|
||||||
request.setPort(2201);
|
request.setPort(2201);
|
||||||
@@ -210,29 +218,31 @@ public class NsdServiceTest {
|
|||||||
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
|
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
|
||||||
client.registerService(request, PROTOCOL, listener1);
|
client.registerService(request, PROTOCOL, listener1);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
verify(mMockMDnsM, times(1)).registerEventListener(any());
|
||||||
verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
|
verify(mMockMDnsM, times(1)).startDaemon();
|
||||||
|
verify(mMockMDnsM, times(1)).registerService(
|
||||||
|
eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
|
||||||
|
|
||||||
// Client discovery request
|
// Client discovery request
|
||||||
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
|
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
|
||||||
client.discoverServices("a_type", PROTOCOL, listener2);
|
client.discoverServices("a_type", PROTOCOL, listener2);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
verify(mMockMDnsM, times(1)).discover(eq(3), eq("a_type"), eq(0));
|
||||||
verifyDaemonCommand("discover 3 a_type 0");
|
|
||||||
|
|
||||||
// Client resolve request
|
// Client resolve request
|
||||||
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
|
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
|
||||||
client.resolveService(request, listener3);
|
client.resolveService(request, listener3);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
verify(mMockMDnsM, times(1)).resolve(
|
||||||
verifyDaemonCommand("resolve 4 a_name a_type local. 0");
|
eq(4), eq("a_name"), eq("a_type"), eq("local."), eq(0));
|
||||||
|
|
||||||
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
|
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
|
||||||
deathRecipient.binderDied();
|
deathRecipient.binderDied();
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
||||||
// checks that request are cleaned
|
// checks that request are cleaned
|
||||||
verifyDaemonCommands("stop-register 2", "stop-discover 3",
|
verify(mMockMDnsM, times(1)).stopOperation(eq(2));
|
||||||
"stop-resolve 4", "stop-service");
|
verify(mMockMDnsM, times(1)).stopOperation(eq(3));
|
||||||
|
verify(mMockMDnsM, times(1)).stopOperation(eq(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -246,20 +256,23 @@ public class NsdServiceTest {
|
|||||||
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
|
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
|
||||||
client.registerService(request, PROTOCOL, listener1);
|
client.registerService(request, PROTOCOL, listener1);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mDaemon, times(1)).maybeStart();
|
verify(mMockMDnsM, times(1)).registerEventListener(any());
|
||||||
|
verify(mMockMDnsM, times(1)).startDaemon();
|
||||||
final INsdManagerCallback cb1 = getCallback();
|
final INsdManagerCallback cb1 = getCallback();
|
||||||
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
|
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
|
||||||
verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
|
verify(mMockMDnsM, times(1)).registerService(
|
||||||
|
eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
|
||||||
|
|
||||||
client.unregisterService(listener1);
|
client.unregisterService(listener1);
|
||||||
verifyDaemonCommand("stop-register 2");
|
waitForIdle();
|
||||||
|
verify(mMockMDnsM, times(1)).stopOperation(eq(2));
|
||||||
|
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
||||||
verifyDaemonCommand("stop-service");
|
reset(mMockMDnsM);
|
||||||
reset(mDaemon);
|
|
||||||
deathRecipient.binderDied();
|
deathRecipient.binderDied();
|
||||||
// Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
|
// Client disconnects, daemon should not be stopped after CLEANUP_DELAY_MS.
|
||||||
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
|
verify(mMockMDnsM, never()).unregisterEventListener(any());
|
||||||
|
verify(mMockMDnsM, never()).stopDaemon();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForIdle() {
|
private void waitForIdle() {
|
||||||
@@ -267,11 +280,7 @@ public class NsdServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NsdService makeService() {
|
NsdService makeService() {
|
||||||
DaemonConnectionSupplier supplier = (callback) -> {
|
final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS) {
|
||||||
mDaemonCallback = callback;
|
|
||||||
return mDaemon;
|
|
||||||
};
|
|
||||||
final NsdService service = new NsdService(mContext, mHandler, supplier, CLEANUP_DELAY_MS) {
|
|
||||||
@Override
|
@Override
|
||||||
public INsdServiceConnector connect(INsdManagerCallback baseCb) {
|
public INsdServiceConnector connect(INsdManagerCallback baseCb) {
|
||||||
// Wrap the callback in a transparent mock, to mock asBinder returning a
|
// Wrap the callback in a transparent mock, to mock asBinder returning a
|
||||||
@@ -285,7 +294,6 @@ public class NsdServiceTest {
|
|||||||
return super.connect(cb);
|
return super.connect(cb);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
verify(mDaemon, never()).execute(any(String.class));
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,34 +305,15 @@ public class NsdServiceTest {
|
|||||||
return new NsdManager(mContext, service);
|
return new NsdManager(mContext, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifyDelayMaybeStopDaemon(long cleanupDelayMs) {
|
void verifyDelayMaybeStopDaemon(long cleanupDelayMs) throws Exception {
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
// Stop daemon shouldn't be called immediately.
|
// Stop daemon shouldn't be called immediately.
|
||||||
verify(mDaemon, never()).maybeStop();
|
verify(mMockMDnsM, never()).unregisterEventListener(any());
|
||||||
|
verify(mMockMDnsM, never()).stopDaemon();
|
||||||
|
|
||||||
// Clean up the daemon after CLEANUP_DELAY_MS.
|
// Clean up the daemon after CLEANUP_DELAY_MS.
|
||||||
verify(mDaemon, timeout(cleanupDelayMs + TIMEOUT_MS)).maybeStop();
|
verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).unregisterEventListener(any());
|
||||||
}
|
verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).stopDaemon();
|
||||||
|
|
||||||
void verifyDaemonCommands(String... wants) {
|
|
||||||
verifyDaemonCommand(String.join(" ", wants), wants.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void verifyDaemonCommand(String want) {
|
|
||||||
verifyDaemonCommand(want, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void verifyDaemonCommand(String want, int n) {
|
|
||||||
waitForIdle();
|
|
||||||
final ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
|
|
||||||
verify(mDaemon, 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);
|
|
||||||
doReturn(true).when(mDaemon).execute(any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestHandler extends Handler {
|
public static class TestHandler extends Handler {
|
||||||
|
|||||||
Reference in New Issue
Block a user