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:
@@ -38,4 +38,8 @@ oneway interface INsdManagerCallback {
|
||||
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
|
||||
void onStopResolutionFailed(int listenerKey, int error);
|
||||
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 startDaemon();
|
||||
void stopResolution(int listenerKey);
|
||||
void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
|
||||
void unregisterServiceInfoCallback(int listenerKey);
|
||||
}
|
||||
@@ -254,6 +254,20 @@ public final class NsdManager {
|
||||
/** @hide */
|
||||
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 */
|
||||
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_FAILED, "STOP_RESOLUTION_FAILED");
|
||||
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 */
|
||||
@@ -617,6 +637,26 @@ public final class NsdManager {
|
||||
public void onStopResolutionSucceeded(int 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;
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {
|
||||
@@ -654,6 +702,15 @@ public final class NsdManager {
|
||||
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 */
|
||||
public interface DiscoveryListener {
|
||||
|
||||
@@ -727,6 +784,54 @@ public final class NsdManager {
|
||||
@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
|
||||
class ServiceHandler extends Handler {
|
||||
ServiceHandler(Looper looper) {
|
||||
@@ -827,6 +932,23 @@ public final class NsdManager {
|
||||
executor.execute(() -> ((ResolveListener) listener).onResolveStopped(
|
||||
ns));
|
||||
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:
|
||||
Log.d(TAG, "Ignored " + message);
|
||||
break;
|
||||
@@ -1138,7 +1260,14 @@ public final class NsdManager {
|
||||
* @param serviceInfo service to be resolved
|
||||
* @param listener to receive callback upon success or failure. Cannot be null.
|
||||
* 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) {
|
||||
resolveService(serviceInfo, Runnable::run, listener);
|
||||
}
|
||||
@@ -1150,7 +1279,14 @@ public final class NsdManager {
|
||||
* @param serviceInfo service to be resolved
|
||||
* @param executor Executor to run listener callbacks with
|
||||
* @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,
|
||||
@NonNull Executor executor, @NonNull ResolveListener listener) {
|
||||
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) {
|
||||
Objects.requireNonNull(listener, "listener cannot be null");
|
||||
}
|
||||
|
||||
@@ -26,10 +26,14 @@ import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.net.module.util.InetAddressUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -46,7 +50,7 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
|
||||
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
|
||||
|
||||
private InetAddress mHost;
|
||||
private final List<InetAddress> mHostAddresses = new ArrayList<>();
|
||||
|
||||
private int mPort;
|
||||
|
||||
@@ -84,17 +88,32 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
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() {
|
||||
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) {
|
||||
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() {
|
||||
return mPort;
|
||||
}
|
||||
@@ -104,6 +123,24 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
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.
|
||||
*
|
||||
@@ -359,7 +396,7 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("name: ").append(mServiceName)
|
||||
.append(", type: ").append(mServiceType)
|
||||
.append(", host: ").append(mHost)
|
||||
.append(", hostAddresses: ").append(TextUtils.join(", ", mHostAddresses))
|
||||
.append(", port: ").append(mPort)
|
||||
.append(", network: ").append(mNetwork);
|
||||
|
||||
@@ -377,12 +414,6 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mServiceName);
|
||||
dest.writeString(mServiceType);
|
||||
if (mHost != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeByteArray(mHost.getAddress());
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeInt(mPort);
|
||||
|
||||
// TXT record key/value pairs.
|
||||
@@ -401,6 +432,10 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
|
||||
dest.writeParcelable(mNetwork, 0);
|
||||
dest.writeInt(mInterfaceIndex);
|
||||
dest.writeInt(mHostAddresses.size());
|
||||
for (InetAddress address : mHostAddresses) {
|
||||
InetAddressUtils.parcelInetAddress(dest, address, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
@@ -410,13 +445,6 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
NsdServiceInfo info = new NsdServiceInfo();
|
||||
info.mServiceName = 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();
|
||||
|
||||
// TXT record key/value pairs.
|
||||
@@ -432,6 +460,10 @@ public final class NsdServiceInfo implements Parcelable {
|
||||
}
|
||||
info.mNetwork = in.readParcelable(null, Network.class);
|
||||
info.mInterfaceIndex = in.readInt();
|
||||
int size = in.readInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
info.mHostAddresses.add(InetAddressUtils.unparcelInetAddress(in));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user