New API to listen service update
Currently, the resolution is a one shot query, it only notifies the first finding service information. There is no way to listen the service update. Thus, add a new API that can register a callback to listen to the service updates continuously. Bug: 245369943 Test: atest FrameworksNetTests CtsNetTestCases Change-Id: I0e9d92b9028375feb3e344ab6c4acb515c5b2be9
This commit is contained in:
@@ -192,14 +192,17 @@ package android.net.nsd {
|
|||||||
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
|
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
|
||||||
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
|
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
|
||||||
method public void registerService(@NonNull android.net.nsd.NsdServiceInfo, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.RegistrationListener);
|
method public void registerService(@NonNull android.net.nsd.NsdServiceInfo, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.RegistrationListener);
|
||||||
method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
|
method public void registerServiceInfoCallback(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ServiceInfoCallback);
|
||||||
method public void resolveService(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ResolveListener);
|
method @Deprecated public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
|
||||||
|
method @Deprecated public void resolveService(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ResolveListener);
|
||||||
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
|
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
|
||||||
method public void stopServiceResolution(@NonNull android.net.nsd.NsdManager.ResolveListener);
|
method public void stopServiceResolution(@NonNull android.net.nsd.NsdManager.ResolveListener);
|
||||||
method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
|
method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
|
||||||
|
method public void unregisterServiceInfoCallback(@NonNull android.net.nsd.NsdManager.ServiceInfoCallback);
|
||||||
field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
|
field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
|
||||||
field public static final String EXTRA_NSD_STATE = "nsd_state";
|
field public static final String EXTRA_NSD_STATE = "nsd_state";
|
||||||
field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3
|
field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3
|
||||||
|
field public static final int FAILURE_BAD_PARAMETERS = 6; // 0x6
|
||||||
field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0
|
field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0
|
||||||
field public static final int FAILURE_MAX_LIMIT = 4; // 0x4
|
field public static final int FAILURE_MAX_LIMIT = 4; // 0x4
|
||||||
field public static final int FAILURE_OPERATION_NOT_RUNNING = 5; // 0x5
|
field public static final int FAILURE_OPERATION_NOT_RUNNING = 5; // 0x5
|
||||||
@@ -231,18 +234,27 @@ package android.net.nsd {
|
|||||||
method public default void onStopResolutionFailed(@NonNull android.net.nsd.NsdServiceInfo, int);
|
method public default void onStopResolutionFailed(@NonNull android.net.nsd.NsdServiceInfo, int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static interface NsdManager.ServiceInfoCallback {
|
||||||
|
method public void onServiceInfoCallbackRegistrationFailed(int);
|
||||||
|
method public void onServiceInfoCallbackUnregistered();
|
||||||
|
method public void onServiceLost();
|
||||||
|
method public void onServiceUpdated(@NonNull android.net.nsd.NsdServiceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public final class NsdServiceInfo implements android.os.Parcelable {
|
public final class NsdServiceInfo implements android.os.Parcelable {
|
||||||
ctor public NsdServiceInfo();
|
ctor public NsdServiceInfo();
|
||||||
method public int describeContents();
|
method public int describeContents();
|
||||||
method public java.util.Map<java.lang.String,byte[]> getAttributes();
|
method public java.util.Map<java.lang.String,byte[]> getAttributes();
|
||||||
method public java.net.InetAddress getHost();
|
method @Deprecated public java.net.InetAddress getHost();
|
||||||
|
method @NonNull public java.util.List<java.net.InetAddress> getHostAddresses();
|
||||||
method @Nullable public android.net.Network getNetwork();
|
method @Nullable public android.net.Network getNetwork();
|
||||||
method public int getPort();
|
method public int getPort();
|
||||||
method public String getServiceName();
|
method public String getServiceName();
|
||||||
method public String getServiceType();
|
method public String getServiceType();
|
||||||
method public void removeAttribute(String);
|
method public void removeAttribute(String);
|
||||||
method public void setAttribute(String, String);
|
method public void setAttribute(String, String);
|
||||||
method public void setHost(java.net.InetAddress);
|
method @Deprecated public void setHost(java.net.InetAddress);
|
||||||
|
method public void setHostAddresses(@NonNull java.util.List<java.net.InetAddress>);
|
||||||
method public void setNetwork(@Nullable android.net.Network);
|
method public void setNetwork(@Nullable android.net.Network);
|
||||||
method public void setPort(int);
|
method public void setPort(int);
|
||||||
method public void setServiceName(String);
|
method public void setServiceName(String);
|
||||||
|
|||||||
@@ -38,4 +38,8 @@ oneway interface INsdManagerCallback {
|
|||||||
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
|
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
|
||||||
void onStopResolutionFailed(int listenerKey, int error);
|
void onStopResolutionFailed(int listenerKey, int error);
|
||||||
void onStopResolutionSucceeded(int listenerKey);
|
void onStopResolutionSucceeded(int listenerKey);
|
||||||
|
void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error);
|
||||||
|
void onServiceUpdated(int listenerKey, in NsdServiceInfo info);
|
||||||
|
void onServiceUpdatedLost(int listenerKey);
|
||||||
|
void onServiceInfoCallbackUnregistered(int listenerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,6 @@ interface INsdServiceConnector {
|
|||||||
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
|
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
|
||||||
void startDaemon();
|
void startDaemon();
|
||||||
void stopResolution(int listenerKey);
|
void stopResolution(int listenerKey);
|
||||||
|
void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
|
||||||
|
void unregisterServiceInfoCallback(int listenerKey);
|
||||||
}
|
}
|
||||||
@@ -254,6 +254,20 @@ public final class NsdManager {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int STOP_RESOLUTION_SUCCEEDED = 26;
|
public static final int STOP_RESOLUTION_SUCCEEDED = 26;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int REGISTER_SERVICE_CALLBACK = 27;
|
||||||
|
/** @hide */
|
||||||
|
public static final int REGISTER_SERVICE_CALLBACK_FAILED = 28;
|
||||||
|
/** @hide */
|
||||||
|
public static final int SERVICE_UPDATED = 29;
|
||||||
|
/** @hide */
|
||||||
|
public static final int SERVICE_UPDATED_LOST = 30;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int UNREGISTER_SERVICE_CALLBACK = 31;
|
||||||
|
/** @hide */
|
||||||
|
public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32;
|
||||||
|
|
||||||
/** 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;
|
||||||
|
|
||||||
@@ -282,6 +296,12 @@ public final class NsdManager {
|
|||||||
EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION");
|
EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION");
|
||||||
EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED");
|
EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED");
|
||||||
EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED");
|
EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED");
|
||||||
|
EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK");
|
||||||
|
EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED");
|
||||||
|
EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
|
||||||
|
EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK");
|
||||||
|
EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED,
|
||||||
|
"UNREGISTER_SERVICE_CALLBACK_SUCCEEDED");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -617,6 +637,26 @@ public final class NsdManager {
|
|||||||
public void onStopResolutionSucceeded(int listenerKey) {
|
public void onStopResolutionSucceeded(int listenerKey) {
|
||||||
sendNoArg(STOP_RESOLUTION_SUCCEEDED, listenerKey);
|
sendNoArg(STOP_RESOLUTION_SUCCEEDED, listenerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
|
||||||
|
sendError(REGISTER_SERVICE_CALLBACK_FAILED, listenerKey, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceUpdated(int listenerKey, NsdServiceInfo info) {
|
||||||
|
sendInfo(SERVICE_UPDATED, listenerKey, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceUpdatedLost(int listenerKey) {
|
||||||
|
sendNoArg(SERVICE_UPDATED_LOST, listenerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceInfoCallbackUnregistered(int listenerKey) {
|
||||||
|
sendNoArg(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, listenerKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -646,6 +686,14 @@ public final class NsdManager {
|
|||||||
*/
|
*/
|
||||||
public static final int FAILURE_OPERATION_NOT_RUNNING = 5;
|
public static final int FAILURE_OPERATION_NOT_RUNNING = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the service has failed to resolve because of bad parameters.
|
||||||
|
*
|
||||||
|
* This failure is passed with
|
||||||
|
* {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed}.
|
||||||
|
*/
|
||||||
|
public static final int FAILURE_BAD_PARAMETERS = 6;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef(value = {
|
@IntDef(value = {
|
||||||
@@ -654,6 +702,15 @@ public final class NsdManager {
|
|||||||
public @interface StopOperationFailureCode {
|
public @interface StopOperationFailureCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef(value = {
|
||||||
|
FAILURE_ALREADY_ACTIVE,
|
||||||
|
FAILURE_BAD_PARAMETERS,
|
||||||
|
})
|
||||||
|
public @interface ResolutionFailureCode {
|
||||||
|
}
|
||||||
|
|
||||||
/** Interface for callback invocation for service discovery */
|
/** Interface for callback invocation for service discovery */
|
||||||
public interface DiscoveryListener {
|
public interface DiscoveryListener {
|
||||||
|
|
||||||
@@ -727,6 +784,54 @@ public final class NsdManager {
|
|||||||
@StopOperationFailureCode int errorCode) { }
|
@StopOperationFailureCode int errorCode) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to listen to service info updates.
|
||||||
|
*
|
||||||
|
* For use with {@link NsdManager#registerServiceInfoCallback} to register, and with
|
||||||
|
* {@link NsdManager#unregisterServiceInfoCallback} to stop listening.
|
||||||
|
*/
|
||||||
|
public interface ServiceInfoCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports that registering the callback failed with an error.
|
||||||
|
*
|
||||||
|
* Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}.
|
||||||
|
*
|
||||||
|
* onServiceInfoCallbackRegistrationFailed will be called exactly once when the callback
|
||||||
|
* could not be registered. No other callback will be sent in that case.
|
||||||
|
*/
|
||||||
|
void onServiceInfoCallbackRegistrationFailed(@ResolutionFailureCode int errorCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports updated service info.
|
||||||
|
*
|
||||||
|
* Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. Any
|
||||||
|
* service updates will be notified via this callback until
|
||||||
|
* {@link NsdManager#unregisterServiceInfoCallback} is called. This will only be called once
|
||||||
|
* the service is found, so may never be called if the service is never present.
|
||||||
|
*/
|
||||||
|
void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports when the service that this callback listens to becomes unavailable.
|
||||||
|
*
|
||||||
|
* Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. The
|
||||||
|
* service may become available again, in which case {@link #onServiceUpdated} will be
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
void onServiceLost();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports that service info updates have stopped.
|
||||||
|
*
|
||||||
|
* Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}.
|
||||||
|
*
|
||||||
|
* A callback unregistration operation will call onServiceInfoCallbackUnregistered
|
||||||
|
* once. After this, the callback may be reused.
|
||||||
|
*/
|
||||||
|
void onServiceInfoCallbackUnregistered();
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
class ServiceHandler extends Handler {
|
class ServiceHandler extends Handler {
|
||||||
ServiceHandler(Looper looper) {
|
ServiceHandler(Looper looper) {
|
||||||
@@ -827,6 +932,23 @@ public final class NsdManager {
|
|||||||
executor.execute(() -> ((ResolveListener) listener).onResolveStopped(
|
executor.execute(() -> ((ResolveListener) listener).onResolveStopped(
|
||||||
ns));
|
ns));
|
||||||
break;
|
break;
|
||||||
|
case REGISTER_SERVICE_CALLBACK_FAILED:
|
||||||
|
removeListener(key);
|
||||||
|
executor.execute(() -> ((ServiceInfoCallback) listener)
|
||||||
|
.onServiceInfoCallbackRegistrationFailed(errorCode));
|
||||||
|
break;
|
||||||
|
case SERVICE_UPDATED:
|
||||||
|
executor.execute(() -> ((ServiceInfoCallback) listener)
|
||||||
|
.onServiceUpdated((NsdServiceInfo) obj));
|
||||||
|
break;
|
||||||
|
case SERVICE_UPDATED_LOST:
|
||||||
|
executor.execute(() -> ((ServiceInfoCallback) listener).onServiceLost());
|
||||||
|
break;
|
||||||
|
case UNREGISTER_SERVICE_CALLBACK_SUCCEEDED:
|
||||||
|
removeListener(key);
|
||||||
|
executor.execute(() -> ((ServiceInfoCallback) listener)
|
||||||
|
.onServiceInfoCallbackUnregistered());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.d(TAG, "Ignored " + message);
|
Log.d(TAG, "Ignored " + message);
|
||||||
break;
|
break;
|
||||||
@@ -1138,7 +1260,14 @@ public final class NsdManager {
|
|||||||
* @param serviceInfo service to be resolved
|
* @param serviceInfo service to be resolved
|
||||||
* @param listener to receive callback upon success or failure. Cannot be null.
|
* @param listener to receive callback upon success or failure. Cannot be null.
|
||||||
* Cannot be in use for an active service resolution.
|
* Cannot be in use for an active service resolution.
|
||||||
|
*
|
||||||
|
* @deprecated the returned ServiceInfo may get stale at any time after resolution, including
|
||||||
|
* immediately after the callback is called, and may not contain some service information that
|
||||||
|
* could be delivered later, like additional host addresses. Prefer using
|
||||||
|
* {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the
|
||||||
|
* state of the service.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
|
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
|
||||||
resolveService(serviceInfo, Runnable::run, listener);
|
resolveService(serviceInfo, Runnable::run, listener);
|
||||||
}
|
}
|
||||||
@@ -1150,7 +1279,14 @@ public final class NsdManager {
|
|||||||
* @param serviceInfo service to be resolved
|
* @param serviceInfo service to be resolved
|
||||||
* @param executor Executor to run listener callbacks with
|
* @param executor Executor to run listener callbacks with
|
||||||
* @param listener to receive callback upon success or failure.
|
* @param listener to receive callback upon success or failure.
|
||||||
|
*
|
||||||
|
* @deprecated the returned ServiceInfo may get stale at any time after resolution, including
|
||||||
|
* immediately after the callback is called, and may not contain some service information that
|
||||||
|
* could be delivered later, like additional host addresses. Prefer using
|
||||||
|
* {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the
|
||||||
|
* state of the service.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void resolveService(@NonNull NsdServiceInfo serviceInfo,
|
public void resolveService(@NonNull NsdServiceInfo serviceInfo,
|
||||||
@NonNull Executor executor, @NonNull ResolveListener listener) {
|
@NonNull Executor executor, @NonNull ResolveListener listener) {
|
||||||
checkServiceInfo(serviceInfo);
|
checkServiceInfo(serviceInfo);
|
||||||
@@ -1185,6 +1321,62 @@ public final class NsdManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback to listen for updates to a service.
|
||||||
|
*
|
||||||
|
* An application can listen to a service to continuously monitor availability of given service.
|
||||||
|
* The callback methods will be called on the passed executor. And service updates are sent with
|
||||||
|
* continuous calls to {@link ServiceInfoCallback#onServiceUpdated}.
|
||||||
|
*
|
||||||
|
* This is different from {@link #resolveService} which provides one shot service information.
|
||||||
|
*
|
||||||
|
* <p> An application can listen to a service once a time. It needs to cancel the registration
|
||||||
|
* before registering other callbacks. Upon failure to register a callback for example if
|
||||||
|
* it's a duplicated registration, the application is notified through
|
||||||
|
* {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with
|
||||||
|
* {@link #FAILURE_BAD_PARAMETERS} or {@link #FAILURE_ALREADY_ACTIVE}.
|
||||||
|
*
|
||||||
|
* @param serviceInfo the service to receive updates for
|
||||||
|
* @param executor Executor to run callbacks with
|
||||||
|
* @param listener to receive callback upon service update
|
||||||
|
*/
|
||||||
|
public void registerServiceInfoCallback(@NonNull NsdServiceInfo serviceInfo,
|
||||||
|
@NonNull Executor executor, @NonNull ServiceInfoCallback listener) {
|
||||||
|
checkServiceInfo(serviceInfo);
|
||||||
|
int key = putListener(listener, executor, serviceInfo);
|
||||||
|
try {
|
||||||
|
mService.registerServiceInfoCallback(key, serviceInfo);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a callback registered with {@link #registerServiceInfoCallback}.
|
||||||
|
*
|
||||||
|
* A successful unregistration is notified with a call to
|
||||||
|
* {@link ServiceInfoCallback#onServiceInfoCallbackUnregistered}. The same callback can only be
|
||||||
|
* reused after this is called.
|
||||||
|
*
|
||||||
|
* <p>If the callback is not already registered, this will throw with
|
||||||
|
* {@link IllegalArgumentException}.
|
||||||
|
*
|
||||||
|
* @param listener This should be a listener object that was passed to
|
||||||
|
* {@link #registerServiceInfoCallback}. It identifies the registration that
|
||||||
|
* should be unregistered and notifies of a successful or unsuccessful stop.
|
||||||
|
* Throws {@code IllegalArgumentException} if the listener was not passed to
|
||||||
|
* {@link #registerServiceInfoCallback} before.
|
||||||
|
*/
|
||||||
|
public void unregisterServiceInfoCallback(@NonNull ServiceInfoCallback listener) {
|
||||||
|
// Will throw IllegalArgumentException if the listener is not known
|
||||||
|
int id = getListenerKey(listener);
|
||||||
|
try {
|
||||||
|
mService.unregisterServiceInfoCallback(id);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void checkListener(Object listener) {
|
private static void checkListener(Object listener) {
|
||||||
Objects.requireNonNull(listener, "listener cannot be null");
|
Objects.requireNonNull(listener, "listener cannot be null");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,14 @@ import android.text.TextUtils;
|
|||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.net.module.util.InetAddressUtils;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +50,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
|
|
||||||
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
|
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
|
||||||
|
|
||||||
private InetAddress mHost;
|
private final List<InetAddress> mHostAddresses = new ArrayList<>();
|
||||||
|
|
||||||
private int mPort;
|
private int mPort;
|
||||||
|
|
||||||
@@ -84,17 +88,32 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
mServiceType = s;
|
mServiceType = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the host address. The host address is valid for a resolved service. */
|
/**
|
||||||
|
* Get the host address. The host address is valid for a resolved service.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #getHostAddresses()} to get the entire list of addresses for the host.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public InetAddress getHost() {
|
public InetAddress getHost() {
|
||||||
return mHost;
|
return mHostAddresses.size() == 0 ? null : mHostAddresses.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the host address */
|
/**
|
||||||
|
* Set the host address
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #setHostAddresses(List)} to set multiple addresses for the host.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setHost(InetAddress s) {
|
public void setHost(InetAddress s) {
|
||||||
mHost = s;
|
setHostAddresses(Collections.singletonList(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get port number. The port number is valid for a resolved service. */
|
/**
|
||||||
|
* Get port number. The port number is valid for a resolved service.
|
||||||
|
*
|
||||||
|
* The port is valid for all addresses.
|
||||||
|
* @see #getHostAddresses()
|
||||||
|
*/
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
return mPort;
|
return mPort;
|
||||||
}
|
}
|
||||||
@@ -104,6 +123,24 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
mPort = p;
|
mPort = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the host addresses.
|
||||||
|
*
|
||||||
|
* All host addresses are valid for the resolved service.
|
||||||
|
* All addresses share the same port
|
||||||
|
* @see #getPort()
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public List<InetAddress> getHostAddresses() {
|
||||||
|
return new ArrayList<>(mHostAddresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the host addresses */
|
||||||
|
public void setHostAddresses(@NonNull List<InetAddress> addresses) {
|
||||||
|
mHostAddresses.clear();
|
||||||
|
mHostAddresses.addAll(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpack txt information from a base-64 encoded byte array.
|
* Unpack txt information from a base-64 encoded byte array.
|
||||||
*
|
*
|
||||||
@@ -359,7 +396,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("name: ").append(mServiceName)
|
sb.append("name: ").append(mServiceName)
|
||||||
.append(", type: ").append(mServiceType)
|
.append(", type: ").append(mServiceType)
|
||||||
.append(", host: ").append(mHost)
|
.append(", hostAddresses: ").append(TextUtils.join(", ", mHostAddresses))
|
||||||
.append(", port: ").append(mPort)
|
.append(", port: ").append(mPort)
|
||||||
.append(", network: ").append(mNetwork);
|
.append(", network: ").append(mNetwork);
|
||||||
|
|
||||||
@@ -377,12 +414,6 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(mServiceName);
|
dest.writeString(mServiceName);
|
||||||
dest.writeString(mServiceType);
|
dest.writeString(mServiceType);
|
||||||
if (mHost != null) {
|
|
||||||
dest.writeInt(1);
|
|
||||||
dest.writeByteArray(mHost.getAddress());
|
|
||||||
} else {
|
|
||||||
dest.writeInt(0);
|
|
||||||
}
|
|
||||||
dest.writeInt(mPort);
|
dest.writeInt(mPort);
|
||||||
|
|
||||||
// TXT record key/value pairs.
|
// TXT record key/value pairs.
|
||||||
@@ -401,6 +432,10 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
|
|
||||||
dest.writeParcelable(mNetwork, 0);
|
dest.writeParcelable(mNetwork, 0);
|
||||||
dest.writeInt(mInterfaceIndex);
|
dest.writeInt(mInterfaceIndex);
|
||||||
|
dest.writeInt(mHostAddresses.size());
|
||||||
|
for (InetAddress address : mHostAddresses) {
|
||||||
|
InetAddressUtils.parcelInetAddress(dest, address, flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implement the Parcelable interface */
|
/** Implement the Parcelable interface */
|
||||||
@@ -410,13 +445,6 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
NsdServiceInfo info = new NsdServiceInfo();
|
NsdServiceInfo info = new NsdServiceInfo();
|
||||||
info.mServiceName = in.readString();
|
info.mServiceName = in.readString();
|
||||||
info.mServiceType = in.readString();
|
info.mServiceType = in.readString();
|
||||||
|
|
||||||
if (in.readInt() == 1) {
|
|
||||||
try {
|
|
||||||
info.mHost = InetAddress.getByAddress(in.createByteArray());
|
|
||||||
} catch (java.net.UnknownHostException e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
info.mPort = in.readInt();
|
info.mPort = in.readInt();
|
||||||
|
|
||||||
// TXT record key/value pairs.
|
// TXT record key/value pairs.
|
||||||
@@ -432,6 +460,10 @@ public final class NsdServiceInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
info.mNetwork = in.readParcelable(null, Network.class);
|
info.mNetwork = in.readParcelable(null, Network.class);
|
||||||
info.mInterfaceIndex = in.readInt();
|
info.mInterfaceIndex = in.readInt();
|
||||||
|
int size = in.readInt();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
info.mHostAddresses.add(InetAddressUtils.unparcelInetAddress(in));
|
||||||
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -412,6 +413,13 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
|
clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NsdManager.REGISTER_SERVICE_CALLBACK:
|
||||||
|
cInfo = getClientInfoForReply(msg);
|
||||||
|
if (cInfo != null) {
|
||||||
|
cInfo.onServiceInfoCallbackRegistrationFailed(
|
||||||
|
clientId, NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case NsdManager.DAEMON_CLEANUP:
|
case NsdManager.DAEMON_CLEANUP:
|
||||||
maybeStopDaemon();
|
maybeStopDaemon();
|
||||||
break;
|
break;
|
||||||
@@ -490,6 +498,11 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
maybeStopMonitoringSocketsIfNoActiveRequest();
|
maybeStopMonitoringSocketsIfNoActiveRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearRegisteredServiceInfo(ClientInfo clientInfo) {
|
||||||
|
clientInfo.mRegisteredService = null;
|
||||||
|
clientInfo.mClientIdForServiceUpdates = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the given service type is valid and construct it to a service type
|
* Check the given service type is valid and construct it to a service type
|
||||||
* which can use for discovery / resolution service.
|
* which can use for discovery / resolution service.
|
||||||
@@ -793,6 +806,56 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
clientInfo.mResolvedService = null;
|
clientInfo.mResolvedService = null;
|
||||||
// TODO: Implement the stop resolution with MdnsDiscoveryManager.
|
// TODO: Implement the stop resolution with MdnsDiscoveryManager.
|
||||||
break;
|
break;
|
||||||
|
case NsdManager.REGISTER_SERVICE_CALLBACK:
|
||||||
|
if (DBG) Log.d(TAG, "Register a service callback");
|
||||||
|
args = (ListenerArgs) msg.obj;
|
||||||
|
clientInfo = mClients.get(args.connector);
|
||||||
|
// If the binder death notification for a INsdManagerCallback was received
|
||||||
|
// before any calls are received by NsdService, the clientInfo would be
|
||||||
|
// cleared and cause NPE. Add a null check here to prevent this corner case.
|
||||||
|
if (clientInfo == null) {
|
||||||
|
Log.e(TAG, "Unknown connector in callback registration");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientInfo.mRegisteredService != null) {
|
||||||
|
clientInfo.onServiceInfoCallbackRegistrationFailed(
|
||||||
|
clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeStartDaemon();
|
||||||
|
id = getUniqueId();
|
||||||
|
if (resolveService(id, args.serviceInfo)) {
|
||||||
|
clientInfo.mRegisteredService = new NsdServiceInfo();
|
||||||
|
clientInfo.mClientIdForServiceUpdates = clientId;
|
||||||
|
storeRequestMap(clientId, id, clientInfo, msg.what);
|
||||||
|
} else {
|
||||||
|
clientInfo.onServiceInfoCallbackRegistrationFailed(
|
||||||
|
clientId, NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NsdManager.UNREGISTER_SERVICE_CALLBACK:
|
||||||
|
if (DBG) Log.d(TAG, "Unregister a service callback");
|
||||||
|
args = (ListenerArgs) msg.obj;
|
||||||
|
clientInfo = mClients.get(args.connector);
|
||||||
|
// If the binder death notification for a INsdManagerCallback was received
|
||||||
|
// before any calls are received by NsdService, the clientInfo would be
|
||||||
|
// cleared and cause NPE. Add a null check here to prevent this corner case.
|
||||||
|
if (clientInfo == null) {
|
||||||
|
Log.e(TAG, "Unknown connector in callback unregistration");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = clientInfo.mClientIds.get(clientId);
|
||||||
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
|
if (stopResolveService(id)) {
|
||||||
|
clientInfo.onServiceInfoCallbackUnregistered(clientId);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to unregister service info callback");
|
||||||
|
}
|
||||||
|
clearRegisteredServiceInfo(clientInfo);
|
||||||
|
break;
|
||||||
case MDNS_SERVICE_EVENT:
|
case MDNS_SERVICE_EVENT:
|
||||||
if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
|
if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
@@ -809,6 +872,19 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return HANDLED;
|
return HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifyResolveFailedResult(boolean isListenedToUpdates, int clientId,
|
||||||
|
ClientInfo clientInfo, int error) {
|
||||||
|
if (isListenedToUpdates) {
|
||||||
|
clientInfo.onServiceInfoCallbackRegistrationFailed(clientId, error);
|
||||||
|
clearRegisteredServiceInfo(clientInfo);
|
||||||
|
} else {
|
||||||
|
// The resolve API always returned FAILURE_INTERNAL_ERROR on error; keep it
|
||||||
|
// for backwards compatibility.
|
||||||
|
clientInfo.onResolveServiceFailed(clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
|
clientInfo.mResolvedService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
|
private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
|
||||||
NsdServiceInfo servInfo;
|
NsdServiceInfo servInfo;
|
||||||
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
|
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
|
||||||
@@ -859,6 +935,8 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
// found services on the same interface index and their network at the time
|
// found services on the same interface index and their network at the time
|
||||||
setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
|
setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
|
||||||
clientInfo.onServiceLost(clientId, servInfo);
|
clientInfo.onServiceLost(clientId, servInfo);
|
||||||
|
// TODO: also support registered service lost when not discovering
|
||||||
|
clientInfo.maybeNotifyRegisteredServiceLost(servInfo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
|
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
|
||||||
@@ -895,10 +973,15 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
String rest = fullName.substring(index);
|
String rest = fullName.substring(index);
|
||||||
String type = rest.replace(".local.", "");
|
String type = rest.replace(".local.", "");
|
||||||
|
|
||||||
clientInfo.mResolvedService.setServiceName(name);
|
final boolean isListenedToUpdates =
|
||||||
clientInfo.mResolvedService.setServiceType(type);
|
clientId == clientInfo.mClientIdForServiceUpdates;
|
||||||
clientInfo.mResolvedService.setPort(info.port);
|
final NsdServiceInfo serviceInfo = isListenedToUpdates
|
||||||
clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
|
? clientInfo.mRegisteredService : clientInfo.mResolvedService;
|
||||||
|
|
||||||
|
serviceInfo.setServiceName(name);
|
||||||
|
serviceInfo.setServiceType(type);
|
||||||
|
serviceInfo.setPort(info.port);
|
||||||
|
serviceInfo.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);
|
||||||
@@ -908,9 +991,8 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
if (getAddrInfo(id2, info.hostname, info.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(
|
notifyResolveFailedResult(isListenedToUpdates, clientId, clientInfo,
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
clientInfo.mResolvedService = null;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -918,17 +1000,17 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
/* NNN resolveId errorCode */
|
/* NNN resolveId errorCode */
|
||||||
stopResolveService(id);
|
stopResolveService(id);
|
||||||
removeRequestMap(clientId, id, clientInfo);
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
clientInfo.mResolvedService = null;
|
notifyResolveFailedResult(
|
||||||
clientInfo.onResolveServiceFailed(
|
clientId == clientInfo.mClientIdForServiceUpdates,
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
break;
|
break;
|
||||||
case IMDnsEventListener.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);
|
||||||
clientInfo.mResolvedService = null;
|
notifyResolveFailedResult(
|
||||||
clientInfo.onResolveServiceFailed(
|
clientId == clientInfo.mClientIdForServiceUpdates,
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
break;
|
break;
|
||||||
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
|
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
|
||||||
/* NNN resolveId hostname ttl addr interfaceIdx netId */
|
/* NNN resolveId hostname ttl addr interfaceIdx netId */
|
||||||
@@ -945,19 +1027,38 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
// If the resolved service is on an interface without a network, consider it
|
// If the resolved service is on an interface without a network, consider it
|
||||||
// as a failure: it would not be usable by apps as they would need
|
// as a failure: it would not be usable by apps as they would need
|
||||||
// privileged permissions.
|
// privileged permissions.
|
||||||
if (netId != NETID_UNSET && serviceHost != null) {
|
if (clientId == clientInfo.mClientIdForServiceUpdates) {
|
||||||
clientInfo.mResolvedService.setHost(serviceHost);
|
if (netId != NETID_UNSET && serviceHost != null) {
|
||||||
setServiceNetworkForCallback(clientInfo.mResolvedService,
|
setServiceNetworkForCallback(clientInfo.mRegisteredService,
|
||||||
netId, info.interfaceIdx);
|
netId, info.interfaceIdx);
|
||||||
clientInfo.onResolveServiceSucceeded(
|
final List<InetAddress> addresses =
|
||||||
clientId, clientInfo.mResolvedService);
|
clientInfo.mRegisteredService.getHostAddresses();
|
||||||
|
addresses.add(serviceHost);
|
||||||
|
clientInfo.mRegisteredService.setHostAddresses(addresses);
|
||||||
|
clientInfo.onServiceUpdated(
|
||||||
|
clientId, clientInfo.mRegisteredService);
|
||||||
|
} else {
|
||||||
|
stopGetAddrInfo(id);
|
||||||
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
|
clearRegisteredServiceInfo(clientInfo);
|
||||||
|
clientInfo.onServiceInfoCallbackRegistrationFailed(
|
||||||
|
clientId, NsdManager.FAILURE_BAD_PARAMETERS);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
clientInfo.onResolveServiceFailed(
|
if (netId != NETID_UNSET && serviceHost != null) {
|
||||||
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
clientInfo.mResolvedService.setHost(serviceHost);
|
||||||
|
setServiceNetworkForCallback(clientInfo.mResolvedService,
|
||||||
|
netId, info.interfaceIdx);
|
||||||
|
clientInfo.onResolveServiceSucceeded(
|
||||||
|
clientId, clientInfo.mResolvedService);
|
||||||
|
} else {
|
||||||
|
clientInfo.onResolveServiceFailed(
|
||||||
|
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
stopGetAddrInfo(id);
|
||||||
|
removeRequestMap(clientId, id, clientInfo);
|
||||||
|
clientInfo.mResolvedService = null;
|
||||||
}
|
}
|
||||||
stopGetAddrInfo(id);
|
|
||||||
removeRequestMap(clientId, id, clientInfo);
|
|
||||||
clientInfo.mResolvedService = null;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1342,6 +1443,20 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
|
NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
|
||||||
|
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
|
||||||
|
NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
|
||||||
|
new ListenerArgs(this, serviceInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterServiceInfoCallback(int listenerKey) {
|
||||||
|
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
|
||||||
|
NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
|
||||||
|
new ListenerArgs(this, null)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startDaemon() {
|
public void startDaemon() {
|
||||||
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
|
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
|
||||||
@@ -1503,6 +1618,11 @@ 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;
|
||||||
|
|
||||||
|
/*** The service that is registered to listen to its updates */
|
||||||
|
private NsdServiceInfo mRegisteredService;
|
||||||
|
/*** The client id that listen to updates */
|
||||||
|
private int mClientIdForServiceUpdates;
|
||||||
|
|
||||||
private ClientInfo(INsdManagerCallback cb) {
|
private ClientInfo(INsdManagerCallback cb) {
|
||||||
mCb = cb;
|
mCb = cb;
|
||||||
if (DBG) Log.d(TAG, "New client");
|
if (DBG) Log.d(TAG, "New client");
|
||||||
@@ -1584,6 +1704,18 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return mClientIds.keyAt(idx);
|
return mClientIds.keyAt(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeNotifyRegisteredServiceLost(@NonNull NsdServiceInfo info) {
|
||||||
|
if (mRegisteredService == null) return;
|
||||||
|
if (!Objects.equals(mRegisteredService.getServiceName(), info.getServiceName())) return;
|
||||||
|
// Resolved services have a leading dot appended at the beginning of their type, but in
|
||||||
|
// discovered info it's at the end
|
||||||
|
if (!Objects.equals(
|
||||||
|
mRegisteredService.getServiceType() + ".", "." + info.getServiceType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onServiceUpdatedLost(mClientIdForServiceUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
|
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
|
||||||
try {
|
try {
|
||||||
mCb.onDiscoverServicesStarted(listenerKey, info);
|
mCb.onDiscoverServicesStarted(listenerKey, info);
|
||||||
@@ -1695,5 +1827,37 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
|
Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
|
||||||
|
try {
|
||||||
|
mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onServiceUpdated(int listenerKey, NsdServiceInfo info) {
|
||||||
|
try {
|
||||||
|
mCb.onServiceUpdated(listenerKey, info);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error calling onServiceUpdated", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onServiceUpdatedLost(int listenerKey) {
|
||||||
|
try {
|
||||||
|
mCb.onServiceUpdatedLost(listenerKey);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error calling onServiceUpdatedLost", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onServiceInfoCallbackUnregistered(int listenerKey) {
|
||||||
|
try {
|
||||||
|
mCb.onServiceInfoCallbackUnregistered(listenerKey);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import android.net.InetAddresses;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@RunWith(DevSdkIgnoreRunner.class)
|
@RunWith(DevSdkIgnoreRunner.class)
|
||||||
@@ -45,6 +47,8 @@ import java.util.Map;
|
|||||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
||||||
public class NsdServiceInfoTest {
|
public class NsdServiceInfoTest {
|
||||||
|
|
||||||
|
private static final InetAddress IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
|
||||||
|
private static final InetAddress IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::");
|
||||||
public final static InetAddress LOCALHOST;
|
public final static InetAddress LOCALHOST;
|
||||||
static {
|
static {
|
||||||
// Because test.
|
// Because test.
|
||||||
@@ -124,6 +128,7 @@ public class NsdServiceInfoTest {
|
|||||||
fullInfo.setServiceType("_kitten._tcp");
|
fullInfo.setServiceType("_kitten._tcp");
|
||||||
fullInfo.setPort(4242);
|
fullInfo.setPort(4242);
|
||||||
fullInfo.setHost(LOCALHOST);
|
fullInfo.setHost(LOCALHOST);
|
||||||
|
fullInfo.setHostAddresses(List.of(IPV4_ADDRESS));
|
||||||
fullInfo.setNetwork(new Network(123));
|
fullInfo.setNetwork(new Network(123));
|
||||||
fullInfo.setInterfaceIndex(456);
|
fullInfo.setInterfaceIndex(456);
|
||||||
checkParcelable(fullInfo);
|
checkParcelable(fullInfo);
|
||||||
@@ -139,6 +144,7 @@ public class NsdServiceInfoTest {
|
|||||||
attributedInfo.setServiceType("_kitten._tcp");
|
attributedInfo.setServiceType("_kitten._tcp");
|
||||||
attributedInfo.setPort(4242);
|
attributedInfo.setPort(4242);
|
||||||
attributedInfo.setHost(LOCALHOST);
|
attributedInfo.setHost(LOCALHOST);
|
||||||
|
fullInfo.setHostAddresses(List.of(IPV6_ADDRESS, IPV4_ADDRESS));
|
||||||
attributedInfo.setAttribute("color", "pink");
|
attributedInfo.setAttribute("color", "pink");
|
||||||
attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
|
attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
|
||||||
attributedInfo.setAttribute("adorable", (String) null);
|
attributedInfo.setAttribute("adorable", (String) null);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
import static android.net.InetAddresses.parseNumericAddress;
|
import static android.net.InetAddresses.parseNumericAddress;
|
||||||
|
import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
|
||||||
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
|
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
|
||||||
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
|
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
@@ -666,6 +668,133 @@ public class NsdServiceTest {
|
|||||||
&& request.getServiceType().equals(ns.getServiceType())));
|
&& request.getServiceType().equals(ns.getServiceType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
|
||||||
|
String serviceType, String address, int port, int interfaceIndex, Network network) {
|
||||||
|
assertEquals(serviceName, info.getServiceName());
|
||||||
|
assertEquals(serviceType, info.getServiceType());
|
||||||
|
assertTrue(info.getHostAddresses().contains(parseNumericAddress(address)));
|
||||||
|
assertEquals(port, info.getPort());
|
||||||
|
assertEquals(network, info.getNetwork());
|
||||||
|
assertEquals(interfaceIndex, info.getInterfaceIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterAndUnregisterServiceInfoCallback() throws RemoteException {
|
||||||
|
final NsdManager client = connectClient(mService);
|
||||||
|
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
|
||||||
|
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
|
||||||
|
NsdManager.ServiceInfoCallback.class);
|
||||||
|
client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
final IMDnsEventListener eventListener = getEventListener();
|
||||||
|
final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
|
||||||
|
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
|
||||||
|
|
||||||
|
// Resolve service successfully.
|
||||||
|
final ResolutionInfo resolutionInfo = new ResolutionInfo(
|
||||||
|
resolvIdCaptor.getValue(),
|
||||||
|
IMDnsEventListener.SERVICE_RESOLVED,
|
||||||
|
null /* serviceName */,
|
||||||
|
null /* serviceType */,
|
||||||
|
null /* domain */,
|
||||||
|
SERVICE_FULL_NAME,
|
||||||
|
DOMAIN_NAME,
|
||||||
|
PORT,
|
||||||
|
new byte[0] /* txtRecord */,
|
||||||
|
IFACE_IDX_ANY);
|
||||||
|
doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
|
||||||
|
eventListener.onServiceResolutionStatus(resolutionInfo);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
|
||||||
|
eq(IFACE_IDX_ANY));
|
||||||
|
|
||||||
|
// First address info
|
||||||
|
final String v4Address = "192.0.2.1";
|
||||||
|
final String v6Address = "2001:db8::";
|
||||||
|
final GetAddressInfo addressInfo1 = new GetAddressInfo(
|
||||||
|
getAddrIdCaptor.getValue(),
|
||||||
|
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
|
||||||
|
SERVICE_FULL_NAME,
|
||||||
|
v4Address,
|
||||||
|
IFACE_IDX_ANY,
|
||||||
|
999 /* netId */);
|
||||||
|
eventListener.onGettingServiceAddressStatus(addressInfo1);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
|
||||||
|
ArgumentCaptor.forClass(NsdServiceInfo.class);
|
||||||
|
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
|
||||||
|
.onServiceUpdated(updateInfoCaptor.capture());
|
||||||
|
verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME,
|
||||||
|
"." + SERVICE_TYPE, v4Address, PORT, IFACE_IDX_ANY, new Network(999));
|
||||||
|
|
||||||
|
// Second address info
|
||||||
|
final GetAddressInfo addressInfo2 = new GetAddressInfo(
|
||||||
|
getAddrIdCaptor.getValue(),
|
||||||
|
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
|
||||||
|
SERVICE_FULL_NAME,
|
||||||
|
v6Address,
|
||||||
|
IFACE_IDX_ANY,
|
||||||
|
999 /* netId */);
|
||||||
|
eventListener.onGettingServiceAddressStatus(addressInfo2);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2))
|
||||||
|
.onServiceUpdated(updateInfoCaptor.capture());
|
||||||
|
verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(1) /* info */, SERVICE_NAME,
|
||||||
|
"." + SERVICE_TYPE, v6Address, PORT, IFACE_IDX_ANY, new Network(999));
|
||||||
|
|
||||||
|
client.unregisterServiceInfoCallback(serviceInfoCallback);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterServiceCallbackFailed() throws Exception {
|
||||||
|
final NsdManager client = connectClient(mService);
|
||||||
|
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
|
||||||
|
final NsdManager.ServiceInfoCallback subscribeListener = mock(
|
||||||
|
NsdManager.ServiceInfoCallback.class);
|
||||||
|
client.registerServiceInfoCallback(request, Runnable::run, subscribeListener);
|
||||||
|
waitForIdle();
|
||||||
|
|
||||||
|
final IMDnsEventListener eventListener = getEventListener();
|
||||||
|
final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
|
||||||
|
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
|
||||||
|
|
||||||
|
// Fail to resolve service.
|
||||||
|
final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
|
||||||
|
resolvIdCaptor.getValue(),
|
||||||
|
IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
|
||||||
|
null /* serviceName */,
|
||||||
|
null /* serviceType */,
|
||||||
|
null /* domain */,
|
||||||
|
null /* serviceFullName */,
|
||||||
|
null /* domainName */,
|
||||||
|
0 /* port */,
|
||||||
|
new byte[0] /* txtRecord */,
|
||||||
|
IFACE_IDX_ANY);
|
||||||
|
eventListener.onServiceResolutionStatus(resolutionFailedInfo);
|
||||||
|
verify(subscribeListener, timeout(TIMEOUT_MS))
|
||||||
|
.onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnregisterNotRegisteredCallback() {
|
||||||
|
final NsdManager client = connectClient(mService);
|
||||||
|
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
|
||||||
|
NsdManager.ServiceInfoCallback.class);
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
client.unregisterServiceInfoCallback(serviceInfoCallback));
|
||||||
|
}
|
||||||
|
|
||||||
private void makeServiceWithMdnsDiscoveryManagerEnabled() {
|
private void makeServiceWithMdnsDiscoveryManagerEnabled() {
|
||||||
doReturn(true).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
|
doReturn(true).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
|
||||||
doReturn(mDiscoveryManager).when(mDeps).makeMdnsDiscoveryManager(any(), any());
|
doReturn(mDiscoveryManager).when(mDeps).makeMdnsDiscoveryManager(any(), any());
|
||||||
|
|||||||
Reference in New Issue
Block a user