Merge changes from topic "mdns-ttl" into main
* changes: [mdns] add service-side impl for NSD service TTL support [mdns] add API for setting/getting TTL value
This commit is contained in:
commit
0520e26936
17 changed files with 604 additions and 101 deletions
|
|
@ -17,11 +17,13 @@ package android.net.nsd;
|
|||
|
||||
import android.annotation.LongDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +36,7 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
/**
|
||||
* Only update the registration without sending exit and re-announcement.
|
||||
*/
|
||||
public static final int NSD_ADVERTISING_UPDATE_ONLY = 1;
|
||||
public static final long NSD_ADVERTISING_UPDATE_ONLY = 1;
|
||||
|
||||
|
||||
@NonNull
|
||||
|
|
@ -46,7 +48,9 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
NsdServiceInfo.class.getClassLoader(), NsdServiceInfo.class);
|
||||
final int protocolType = in.readInt();
|
||||
final long advertiseConfig = in.readLong();
|
||||
return new AdvertisingRequest(serviceInfo, protocolType, advertiseConfig);
|
||||
final long ttlSeconds = in.readLong();
|
||||
final Duration ttl = ttlSeconds < 0 ? null : Duration.ofSeconds(ttlSeconds);
|
||||
return new AdvertisingRequest(serviceInfo, protocolType, advertiseConfig, ttl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -60,6 +64,9 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
// Bitmask of @AdvertisingConfig flags. Uses a long to allow 64 possible flags in the future.
|
||||
private final long mAdvertisingConfig;
|
||||
|
||||
@Nullable
|
||||
private final Duration mTtl;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
|
@ -73,10 +80,11 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
* The constructor for the advertiseRequest
|
||||
*/
|
||||
private AdvertisingRequest(@NonNull NsdServiceInfo serviceInfo, int protocolType,
|
||||
long advertisingConfig) {
|
||||
long advertisingConfig, @NonNull Duration ttl) {
|
||||
mServiceInfo = serviceInfo;
|
||||
mProtocolType = protocolType;
|
||||
mAdvertisingConfig = advertisingConfig;
|
||||
mTtl = ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -101,12 +109,25 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
return mAdvertisingConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time interval that the resource records may be cached on a DNS resolver or
|
||||
* {@code null} if not specified.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// @FlaggedApi(NsdManager.Flags.NSD_CUSTOM_TTL_ENABLED)
|
||||
@Nullable
|
||||
public Duration getTtl() {
|
||||
return mTtl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("serviceInfo: ").append(mServiceInfo)
|
||||
.append(", protocolType: ").append(mProtocolType)
|
||||
.append(", advertisingConfig: ").append(mAdvertisingConfig);
|
||||
.append(", advertisingConfig: ").append(mAdvertisingConfig)
|
||||
.append(", ttl: ").append(mTtl);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
|
@ -120,13 +141,14 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
final AdvertisingRequest otherRequest = (AdvertisingRequest) other;
|
||||
return mServiceInfo.equals(otherRequest.mServiceInfo)
|
||||
&& mProtocolType == otherRequest.mProtocolType
|
||||
&& mAdvertisingConfig == otherRequest.mAdvertisingConfig;
|
||||
&& mAdvertisingConfig == otherRequest.mAdvertisingConfig
|
||||
&& Objects.equals(mTtl, otherRequest.mTtl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mServiceInfo, mProtocolType, mAdvertisingConfig);
|
||||
return Objects.hash(mServiceInfo, mProtocolType, mAdvertisingConfig, mTtl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -139,6 +161,7 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
dest.writeParcelable(mServiceInfo, flags);
|
||||
dest.writeInt(mProtocolType);
|
||||
dest.writeLong(mAdvertisingConfig);
|
||||
dest.writeLong(mTtl == null ? -1 : mTtl.getSeconds());
|
||||
}
|
||||
|
||||
// @FlaggedApi(NsdManager.Flags.ADVERTISE_REQUEST_API)
|
||||
|
|
@ -151,6 +174,8 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
private final NsdServiceInfo mServiceInfo;
|
||||
private final int mProtocolType;
|
||||
private long mAdvertisingConfig;
|
||||
@Nullable
|
||||
private Duration mTtl;
|
||||
/**
|
||||
* Creates a new {@link Builder} object.
|
||||
*/
|
||||
|
|
@ -170,11 +195,44 @@ public final class AdvertisingRequest implements Parcelable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time interval that the resource records may be cached on a DNS resolver.
|
||||
*
|
||||
* If this method is not called or {@code ttl} is {@code null}, default TTL values
|
||||
* will be used for the service when it's registered. Otherwise, the {@code ttl}
|
||||
* will be used for all resource records of this service.
|
||||
*
|
||||
* When registering a service, {@link NsdManager#FAILURE_BAD_PARAMETERS} will be returned
|
||||
* if {@code ttl} is smaller than 30 seconds.
|
||||
*
|
||||
* Note: only number of seconds of {@code ttl} is used.
|
||||
*
|
||||
* @param ttl the maximum duration that the DNS resource records will be cached
|
||||
*
|
||||
* @see AdvertisingRequest#getTtl
|
||||
* @hide
|
||||
*/
|
||||
// @FlaggedApi(NsdManager.Flags.NSD_CUSTOM_TTL_ENABLED)
|
||||
@NonNull
|
||||
public Builder setTtl(@Nullable Duration ttl) {
|
||||
if (ttl == null) {
|
||||
mTtl = null;
|
||||
return this;
|
||||
}
|
||||
final long ttlSeconds = ttl.getSeconds();
|
||||
if (ttlSeconds < 0 || ttlSeconds > 0xffffffffL) {
|
||||
throw new IllegalArgumentException(
|
||||
"ttlSeconds exceeds the allowed range (value = " + ttlSeconds
|
||||
+ ", allowedRanged = [0, 0xffffffffL])");
|
||||
}
|
||||
mTtl = Duration.ofSeconds(ttlSeconds);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Creates a new {@link AdvertisingRequest} object. */
|
||||
@NonNull
|
||||
public AdvertisingRequest build() {
|
||||
return new AdvertisingRequest(mServiceInfo, mProtocolType, mAdvertisingConfig);
|
||||
return new AdvertisingRequest(mServiceInfo, mProtocolType, mAdvertisingConfig, mTtl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ public final class NsdManager {
|
|||
"com.android.net.flags.advertise_request_api";
|
||||
static final String NSD_CUSTOM_HOSTNAME_ENABLED =
|
||||
"com.android.net.flags.nsd_custom_hostname_enabled";
|
||||
static final String NSD_CUSTOM_TTL_ENABLED =
|
||||
"com.android.net.flags.nsd_custom_ttl_enabled";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -327,6 +329,20 @@ public final class NsdManager {
|
|||
/** Dns based service discovery protocol */
|
||||
public static final int PROTOCOL_DNS_SD = 0x0001;
|
||||
|
||||
/**
|
||||
* The minimum TTL seconds which is allowed for a service registration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final long TTL_SECONDS_MIN = 30L;
|
||||
|
||||
/**
|
||||
* The maximum TTL seconds which is allowed for a service registration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final long TTL_SECONDS_MAX = 10 * 3600L;
|
||||
|
||||
private static final SparseArray<String> EVENT_NAMES = new SparseArray<>();
|
||||
static {
|
||||
EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES");
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package android.net.nsd;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import android.annotation.FlaggedApi;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
|
@ -35,6 +33,7 @@ import com.android.net.module.util.InetAddressUtils;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -71,6 +70,10 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
|
||||
private int mInterfaceIndex;
|
||||
|
||||
// The timestamp that all resource records associated with this service are considered invalid.
|
||||
@Nullable
|
||||
private Instant mExpirationTime;
|
||||
|
||||
public NsdServiceInfo() {
|
||||
mSubtypes = new ArraySet<>();
|
||||
mTxtRecord = new ArrayMap<>();
|
||||
|
|
@ -99,6 +102,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
mPort = other.getPort();
|
||||
mNetwork = other.getNetwork();
|
||||
mInterfaceIndex = other.getInterfaceIndex();
|
||||
mExpirationTime = other.getExpirationTime();
|
||||
}
|
||||
|
||||
/** Get the service name */
|
||||
|
|
@ -490,6 +494,38 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
return Collections.unmodifiableSet(mSubtypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timestamp after when this service is expired.
|
||||
*
|
||||
* Note: only number of seconds of {@code expirationTime} is used.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setExpirationTime(@Nullable Instant expirationTime) {
|
||||
if (expirationTime == null) {
|
||||
mExpirationTime = null;
|
||||
} else {
|
||||
mExpirationTime = Instant.ofEpochSecond(expirationTime.getEpochSecond());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp after when this service is expired or {@code null} if it's unknown.
|
||||
*
|
||||
* A service is considered expired if any of its DNS record is expired.
|
||||
*
|
||||
* Clients that are depending on the refreshness of the service information should not continue
|
||||
* use this service after the returned timestamp. Instead, clients may re-send queries for the
|
||||
* service to get updated the service information.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// @FlaggedApi(NsdManager.Flags.NSD_CUSTOM_TTL_ENABLED)
|
||||
@Nullable
|
||||
public Instant getExpirationTime() {
|
||||
return mExpirationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
@ -499,7 +535,8 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
.append(", hostAddresses: ").append(TextUtils.join(", ", mHostAddresses))
|
||||
.append(", hostname: ").append(mHostname)
|
||||
.append(", port: ").append(mPort)
|
||||
.append(", network: ").append(mNetwork);
|
||||
.append(", network: ").append(mNetwork)
|
||||
.append(", expirationTime: ").append(mExpirationTime);
|
||||
|
||||
byte[] txtRecord = getTxtRecord();
|
||||
sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
|
||||
|
|
@ -539,6 +576,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
InetAddressUtils.parcelInetAddress(dest, address, flags);
|
||||
}
|
||||
dest.writeString(mHostname);
|
||||
dest.writeLong(mExpirationTime != null ? mExpirationTime.getEpochSecond() : -1);
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
|
|
@ -569,6 +607,8 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
info.mHostAddresses.add(InetAddressUtils.unparcelInetAddress(in));
|
||||
}
|
||||
info.mHostname = in.readString();
|
||||
final long seconds = in.readLong();
|
||||
info.setExpirationTime(seconds < 0 ? null : Instant.ofEpochSecond(seconds));
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
|
|||
import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
|
||||
import static android.net.nsd.NsdManager.SUBTYPE_LABEL_REGEX;
|
||||
import static android.net.nsd.NsdManager.TYPE_REGEX;
|
||||
import static android.os.Process.SYSTEM_UID;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
|
||||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
|
||||
|
|
@ -116,6 +117,7 @@ import java.net.InetAddress;
|
|||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
|
@ -739,6 +741,33 @@ public class NsdService extends INsdManager.Stub {
|
|||
return new ArraySet<>(subtypeMap.values());
|
||||
}
|
||||
|
||||
private boolean checkTtl(
|
||||
@Nullable Duration ttl, @NonNull ClientInfo clientInfo) {
|
||||
if (ttl == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final long ttlSeconds = ttl.toSeconds();
|
||||
final int uid = clientInfo.getUid();
|
||||
|
||||
// Allows Thread module in the system_server to register TTL that is smaller than
|
||||
// 30 seconds
|
||||
final long minTtlSeconds = uid == SYSTEM_UID ? 0 : NsdManager.TTL_SECONDS_MIN;
|
||||
|
||||
// Allows Thread module in the system_server to register TTL that is larger than
|
||||
// 10 hours
|
||||
final long maxTtlSeconds =
|
||||
uid == SYSTEM_UID ? 0xffffffffL : NsdManager.TTL_SECONDS_MAX;
|
||||
|
||||
if (ttlSeconds < minTtlSeconds || ttlSeconds > maxTtlSeconds) {
|
||||
mServiceLogs.e("ttlSeconds exceeds allowed range (value = "
|
||||
+ ttlSeconds + ", allowedRange = [" + minTtlSeconds
|
||||
+ ", " + maxTtlSeconds + " ])");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Message msg) {
|
||||
final ClientInfo clientInfo;
|
||||
|
|
@ -965,11 +994,19 @@ public class NsdService extends INsdManager.Stub {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!checkTtl(advertisingRequest.getTtl(), clientInfo)) {
|
||||
clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
|
||||
NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
|
||||
break;
|
||||
}
|
||||
|
||||
serviceInfo.setSubtypes(subtypes);
|
||||
maybeStartMonitoringSockets();
|
||||
final MdnsAdvertisingOptions mdnsAdvertisingOptions =
|
||||
MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(
|
||||
isUpdateOnly).build();
|
||||
MdnsAdvertisingOptions.newBuilder()
|
||||
.setIsOnlyUpdate(isUpdateOnly)
|
||||
.setTtl(advertisingRequest.getTtl())
|
||||
.build();
|
||||
mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
|
||||
mdnsAdvertisingOptions, clientInfo.mUid);
|
||||
storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
|
||||
|
|
@ -1512,6 +1549,7 @@ public class NsdService extends INsdManager.Stub {
|
|||
network == null ? INetd.LOCAL_NET_ID : network.netId,
|
||||
serviceInfo.getInterfaceIndex());
|
||||
servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
|
||||
servInfo.setExpirationTime(serviceInfo.getExpirationTime());
|
||||
return servInfo;
|
||||
}
|
||||
|
||||
|
|
@ -2680,6 +2718,10 @@ public class NsdService extends INsdManager.Stub {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
private boolean isPreSClient() {
|
||||
return mIsPreSClient;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -449,7 +449,8 @@ public class MdnsAdvertiser {
|
|||
mPendingRegistrations.put(id, registration);
|
||||
for (int i = 0; i < mAdvertisers.size(); i++) {
|
||||
try {
|
||||
mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo());
|
||||
mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo(),
|
||||
registration.getAdvertisingOptions());
|
||||
} catch (NameConflictException e) {
|
||||
mSharedLog.wtf("Name conflict adding services that should have unique names",
|
||||
e);
|
||||
|
|
@ -515,7 +516,7 @@ public class MdnsAdvertiser {
|
|||
final Registration registration = mPendingRegistrations.valueAt(i);
|
||||
try {
|
||||
advertiser.addService(mPendingRegistrations.keyAt(i),
|
||||
registration.getServiceInfo());
|
||||
registration.getServiceInfo(), registration.getAdvertisingOptions());
|
||||
} catch (NameConflictException e) {
|
||||
mSharedLog.wtf("Name conflict adding services that should have unique names",
|
||||
e);
|
||||
|
|
@ -587,15 +588,17 @@ public class MdnsAdvertiser {
|
|||
@NonNull
|
||||
private NsdServiceInfo mServiceInfo;
|
||||
final int mClientUid;
|
||||
private final MdnsAdvertisingOptions mAdvertisingOptions;
|
||||
int mConflictDuringProbingCount;
|
||||
int mConflictAfterProbingCount;
|
||||
|
||||
|
||||
private Registration(@NonNull NsdServiceInfo serviceInfo, int clientUid) {
|
||||
private Registration(@NonNull NsdServiceInfo serviceInfo, int clientUid,
|
||||
@NonNull MdnsAdvertisingOptions advertisingOptions) {
|
||||
this.mOriginalServiceName = serviceInfo.getServiceName();
|
||||
this.mOriginalHostname = serviceInfo.getHostname();
|
||||
this.mServiceInfo = serviceInfo;
|
||||
this.mClientUid = clientUid;
|
||||
this.mAdvertisingOptions = advertisingOptions;
|
||||
}
|
||||
|
||||
/** Check if the new {@link NsdServiceInfo} doesn't update any data other than subtypes. */
|
||||
|
|
@ -697,6 +700,11 @@ public class MdnsAdvertiser {
|
|||
public NsdServiceInfo getServiceInfo() {
|
||||
return mServiceInfo;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MdnsAdvertisingOptions getAdvertisingOptions() {
|
||||
return mAdvertisingOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -855,7 +863,7 @@ public class MdnsAdvertiser {
|
|||
}
|
||||
mSharedLog.i("Adding service " + service + " with ID " + id + " and subtypes "
|
||||
+ subtypes + " advertisingOptions " + advertisingOptions);
|
||||
registration = new Registration(service, clientUid);
|
||||
registration = new Registration(service, clientUid, advertisingOptions);
|
||||
final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
|
||||
if (network == null) {
|
||||
// If registering on all networks, no advertiser must have conflicts
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
package com.android.server.connectivity.mdns;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* API configuration parameters for advertising the mDNS service.
|
||||
*
|
||||
|
|
@ -27,13 +32,15 @@ public class MdnsAdvertisingOptions {
|
|||
|
||||
private static MdnsAdvertisingOptions sDefaultOptions;
|
||||
private final boolean mIsOnlyUpdate;
|
||||
@Nullable
|
||||
private final Duration mTtl;
|
||||
|
||||
/**
|
||||
* Parcelable constructs for a {@link MdnsAdvertisingOptions}.
|
||||
*/
|
||||
MdnsAdvertisingOptions(
|
||||
boolean isOnlyUpdate) {
|
||||
MdnsAdvertisingOptions(boolean isOnlyUpdate, @Nullable Duration ttl) {
|
||||
this.mIsOnlyUpdate = isOnlyUpdate;
|
||||
this.mTtl = ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,9 +67,36 @@ public class MdnsAdvertisingOptions {
|
|||
return mIsOnlyUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TTL for all records in a service.
|
||||
*/
|
||||
@Nullable
|
||||
public Duration getTtl() {
|
||||
return mTtl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
} else if (!(other instanceof MdnsAdvertisingOptions)) {
|
||||
return false;
|
||||
} else {
|
||||
final MdnsAdvertisingOptions otherOptions = (MdnsAdvertisingOptions) other;
|
||||
return mIsOnlyUpdate == otherOptions.mIsOnlyUpdate
|
||||
&& Objects.equals(mTtl, otherOptions.mTtl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mIsOnlyUpdate, mTtl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MdnsAdvertisingOptions{" + "mIsOnlyUpdate=" + mIsOnlyUpdate + '}';
|
||||
return "MdnsAdvertisingOptions{" + "mIsOnlyUpdate=" + mIsOnlyUpdate + ", mTtl=" + mTtl
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +104,8 @@ public class MdnsAdvertisingOptions {
|
|||
*/
|
||||
public static final class Builder {
|
||||
private boolean mIsOnlyUpdate = false;
|
||||
@Nullable
|
||||
private Duration mTtl;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
|
@ -82,11 +118,19 @@ public class MdnsAdvertisingOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TTL duration for all records of the service.
|
||||
*/
|
||||
public Builder setTtl(@Nullable Duration ttl) {
|
||||
this.mTtl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link MdnsAdvertisingOptions} with the arguments supplied to this builder.
|
||||
*/
|
||||
public MdnsAdvertisingOptions build() {
|
||||
return new MdnsAdvertisingOptions(mIsOnlyUpdate);
|
||||
return new MdnsAdvertisingOptions(mIsOnlyUpdate, mTtl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,8 +258,10 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||
*
|
||||
* @throws NameConflictException There is already a service being advertised with that name.
|
||||
*/
|
||||
public void addService(int id, NsdServiceInfo service) throws NameConflictException {
|
||||
final int replacedExitingService = mRecordRepository.addService(id, service);
|
||||
public void addService(int id, NsdServiceInfo service,
|
||||
@NonNull MdnsAdvertisingOptions advertisingOptions) throws NameConflictException {
|
||||
final int replacedExitingService =
|
||||
mRecordRepository.addService(id, service, advertisingOptions.getTtl());
|
||||
// Cancel announcements for the existing service. This only happens for exiting services
|
||||
// (so cancelling exiting announcements), as per RecordRepository.addService.
|
||||
if (replacedExitingService >= 0) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import java.net.Inet4Address;
|
|||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
@ -75,9 +76,9 @@ public class MdnsRecordRepository {
|
|||
// TTL for records with a host name as the resource record's name (e.g., A, AAAA, HINFO) or a
|
||||
// host name contained within the resource record's rdata (e.g., SRV, reverse mapping PTR
|
||||
// record)
|
||||
private static final long NAME_RECORDS_TTL_MILLIS = TimeUnit.SECONDS.toMillis(120);
|
||||
private static final long DEFAULT_NAME_RECORDS_TTL_MILLIS = TimeUnit.SECONDS.toMillis(120);
|
||||
// TTL for other records
|
||||
private static final long NON_NAME_RECORDS_TTL_MILLIS = TimeUnit.MINUTES.toMillis(75);
|
||||
private static final long DEFAULT_NON_NAME_RECORDS_TTL_MILLIS = TimeUnit.MINUTES.toMillis(75);
|
||||
|
||||
// Top-level domain for link-local queries, as per RFC6762 3.
|
||||
private static final String LOCAL_TLD = "local";
|
||||
|
|
@ -193,6 +194,9 @@ public class MdnsRecordRepository {
|
|||
*/
|
||||
private boolean isProbing;
|
||||
|
||||
@Nullable
|
||||
private Duration ttl;
|
||||
|
||||
/**
|
||||
* Create a ServiceRegistration with only update the subType.
|
||||
*/
|
||||
|
|
@ -200,16 +204,32 @@ public class MdnsRecordRepository {
|
|||
NsdServiceInfo newServiceInfo = new NsdServiceInfo(serviceInfo);
|
||||
newServiceInfo.setSubtypes(newSubtypes);
|
||||
return new ServiceRegistration(srvRecord.record.getServiceHost(), newServiceInfo,
|
||||
repliedServiceCount, sentPacketCount, exiting, isProbing);
|
||||
repliedServiceCount, sentPacketCount, exiting, isProbing, ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ServiceRegistration for dns-sd service registration (RFC6763).
|
||||
*/
|
||||
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
|
||||
int repliedServiceCount, int sentPacketCount, boolean exiting, boolean isProbing) {
|
||||
int repliedServiceCount, int sentPacketCount, boolean exiting, boolean isProbing,
|
||||
@Nullable Duration ttl) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
|
||||
final long nonNameRecordsTtlMillis;
|
||||
final long nameRecordsTtlMillis;
|
||||
|
||||
// When custom TTL is specified, all records of the service will use the custom TTL.
|
||||
// This is typically useful for SRP (Service Registration Protocol:
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-dnssd-srp-24) Advertising Proxy
|
||||
// where all records in a single SRP are required the same TTL.
|
||||
if (ttl != null) {
|
||||
nonNameRecordsTtlMillis = ttl.toMillis();
|
||||
nameRecordsTtlMillis = ttl.toMillis();
|
||||
} else {
|
||||
nonNameRecordsTtlMillis = DEFAULT_NON_NAME_RECORDS_TTL_MILLIS;
|
||||
nameRecordsTtlMillis = DEFAULT_NAME_RECORDS_TTL_MILLIS;
|
||||
}
|
||||
|
||||
final boolean hasService = !TextUtils.isEmpty(serviceInfo.getServiceType());
|
||||
final boolean hasCustomHost = !TextUtils.isEmpty(serviceInfo.getHostname());
|
||||
final String[] hostname =
|
||||
|
|
@ -229,7 +249,7 @@ public class MdnsRecordRepository {
|
|||
serviceType,
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
nonNameRecordsTtlMillis,
|
||||
serviceName),
|
||||
true /* sharedName */));
|
||||
for (String subtype : serviceInfo.getSubtypes()) {
|
||||
|
|
@ -239,7 +259,7 @@ public class MdnsRecordRepository {
|
|||
MdnsUtils.constructFullSubtype(serviceType, subtype),
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
nonNameRecordsTtlMillis,
|
||||
serviceName),
|
||||
true /* sharedName */));
|
||||
}
|
||||
|
|
@ -249,7 +269,7 @@ public class MdnsRecordRepository {
|
|||
new MdnsServiceRecord(serviceName,
|
||||
0L /* receiptTimeMillis */,
|
||||
true /* cacheFlush */,
|
||||
NAME_RECORDS_TTL_MILLIS,
|
||||
nameRecordsTtlMillis,
|
||||
0 /* servicePriority */, 0 /* serviceWeight */,
|
||||
serviceInfo.getPort(),
|
||||
hostname),
|
||||
|
|
@ -261,7 +281,7 @@ public class MdnsRecordRepository {
|
|||
0L /* receiptTimeMillis */,
|
||||
// Service name is verified unique after probing
|
||||
true /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
nonNameRecordsTtlMillis,
|
||||
attrsToTextEntries(serviceInfo.getAttributes())),
|
||||
false /* sharedName */);
|
||||
|
||||
|
|
@ -275,7 +295,7 @@ public class MdnsRecordRepository {
|
|||
DNS_SD_SERVICE_TYPE,
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
nonNameRecordsTtlMillis,
|
||||
serviceType),
|
||||
true /* sharedName */));
|
||||
} else {
|
||||
|
|
@ -292,7 +312,7 @@ public class MdnsRecordRepository {
|
|||
new MdnsInetAddressRecord(hostname,
|
||||
0L /* receiptTimeMillis */,
|
||||
true /* cacheFlush */,
|
||||
NAME_RECORDS_TTL_MILLIS,
|
||||
nameRecordsTtlMillis,
|
||||
address),
|
||||
false /* sharedName */));
|
||||
}
|
||||
|
|
@ -315,9 +335,9 @@ public class MdnsRecordRepository {
|
|||
* @param serviceInfo Service to advertise
|
||||
*/
|
||||
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
|
||||
int repliedServiceCount, int sentPacketCount) {
|
||||
int repliedServiceCount, int sentPacketCount, @Nullable Duration ttl) {
|
||||
this(deviceHostname, serviceInfo,repliedServiceCount, sentPacketCount,
|
||||
false /* exiting */, true /* isProbing */);
|
||||
false /* exiting */, true /* isProbing */, ttl);
|
||||
}
|
||||
|
||||
void setProbing(boolean probing) {
|
||||
|
|
@ -339,7 +359,7 @@ public class MdnsRecordRepository {
|
|||
revDnsAddr,
|
||||
0L /* receiptTimeMillis */,
|
||||
true /* cacheFlush */,
|
||||
NAME_RECORDS_TTL_MILLIS,
|
||||
DEFAULT_NAME_RECORDS_TTL_MILLIS,
|
||||
mDeviceHostname),
|
||||
false /* sharedName */));
|
||||
|
||||
|
|
@ -349,7 +369,7 @@ public class MdnsRecordRepository {
|
|||
mDeviceHostname,
|
||||
0L /* receiptTimeMillis */,
|
||||
true /* cacheFlush */,
|
||||
NAME_RECORDS_TTL_MILLIS,
|
||||
DEFAULT_NAME_RECORDS_TTL_MILLIS,
|
||||
addr.getAddress()),
|
||||
false /* sharedName */));
|
||||
}
|
||||
|
|
@ -378,11 +398,13 @@ public class MdnsRecordRepository {
|
|||
* This may remove/replace any existing service that used the name added but is exiting.
|
||||
* @param serviceId A unique service ID.
|
||||
* @param serviceInfo Service info to add.
|
||||
* @param ttl the TTL duration for all records of {@code serviceInfo} or {@code null}
|
||||
* @return If the added service replaced another with a matching name (which was exiting), the
|
||||
* ID of the replaced service.
|
||||
* @throws NameConflictException There is already a (non-exiting) service using the name.
|
||||
*/
|
||||
public int addService(int serviceId, NsdServiceInfo serviceInfo) throws NameConflictException {
|
||||
public int addService(int serviceId, NsdServiceInfo serviceInfo, @Nullable Duration ttl)
|
||||
throws NameConflictException {
|
||||
if (mServices.contains(serviceId)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Service ID must not be reused across registrations: " + serviceId);
|
||||
|
|
@ -397,7 +419,7 @@ public class MdnsRecordRepository {
|
|||
|
||||
final ServiceRegistration registration = new ServiceRegistration(
|
||||
mDeviceHostname, serviceInfo, NO_PACKET /* repliedServiceCount */,
|
||||
NO_PACKET /* sentPacketCount */);
|
||||
NO_PACKET /* sentPacketCount */, ttl);
|
||||
mServices.put(serviceId, registration);
|
||||
|
||||
// Remove existing exiting service
|
||||
|
|
@ -776,7 +798,7 @@ public class MdnsRecordRepository {
|
|||
true /* cacheFlush */,
|
||||
// TODO: RFC6762 6.1: "In general, the TTL given for an NSEC record SHOULD
|
||||
// be the same as the TTL that the record would have had, had it existed."
|
||||
NAME_RECORDS_TTL_MILLIS,
|
||||
DEFAULT_NAME_RECORDS_TTL_MILLIS,
|
||||
question.getName(),
|
||||
new int[] { question.getType() });
|
||||
additionalAnswerInfo.add(
|
||||
|
|
@ -1211,7 +1233,7 @@ public class MdnsRecordRepository {
|
|||
if (existing == null) return null;
|
||||
|
||||
final ServiceRegistration newService = new ServiceRegistration(mDeviceHostname, newInfo,
|
||||
existing.repliedServiceCount, existing.sentPacketCount);
|
||||
existing.repliedServiceCount, existing.sentPacketCount, existing.ttl);
|
||||
mServices.put(serviceId, newService);
|
||||
return makeProbingInfo(serviceId, newService);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import android.text.TextUtils;
|
|||
import com.android.net.module.util.ByteUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
@ -62,7 +63,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
source.createStringArrayList(),
|
||||
source.createTypedArrayList(TextEntry.CREATOR),
|
||||
source.readInt(),
|
||||
source.readParcelable(null));
|
||||
source.readParcelable(Network.class.getClassLoader()),
|
||||
Instant.ofEpochSecond(source.readLong()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -89,6 +91,9 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
@Nullable
|
||||
private final Network network;
|
||||
|
||||
@NonNull
|
||||
private final Instant expirationTime;
|
||||
|
||||
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
||||
public MdnsServiceInfo(
|
||||
String serviceInstanceName,
|
||||
|
|
@ -110,7 +115,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
textStrings,
|
||||
/* textEntries= */ null,
|
||||
/* interfaceIndex= */ INTERFACE_INDEX_UNSPECIFIED,
|
||||
/* network= */ null);
|
||||
/* network= */ null,
|
||||
/* expirationTime= */ Instant.MAX);
|
||||
}
|
||||
|
||||
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
||||
|
|
@ -135,7 +141,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
textStrings,
|
||||
textEntries,
|
||||
/* interfaceIndex= */ INTERFACE_INDEX_UNSPECIFIED,
|
||||
/* network= */ null);
|
||||
/* network= */ null,
|
||||
/* expirationTime= */ Instant.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,7 +172,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
textStrings,
|
||||
textEntries,
|
||||
interfaceIndex,
|
||||
/* network= */ null);
|
||||
/* network= */ null,
|
||||
/* expirationTime= */ Instant.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,7 +192,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
@Nullable List<String> textStrings,
|
||||
@Nullable List<TextEntry> textEntries,
|
||||
int interfaceIndex,
|
||||
@Nullable Network network) {
|
||||
@Nullable Network network,
|
||||
@NonNull Instant expirationTime) {
|
||||
this.serviceInstanceName = serviceInstanceName;
|
||||
this.serviceType = serviceType;
|
||||
this.subtypes = new ArrayList<>();
|
||||
|
|
@ -217,6 +226,7 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
this.attributes = Collections.unmodifiableMap(attributes);
|
||||
this.interfaceIndex = interfaceIndex;
|
||||
this.network = network;
|
||||
this.expirationTime = Instant.ofEpochSecond(expirationTime.getEpochSecond());
|
||||
}
|
||||
|
||||
private static List<TextEntry> parseTextStrings(List<String> textStrings) {
|
||||
|
|
@ -313,6 +323,17 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp after when this service is expired or {@code null} if the expiration
|
||||
* time is unknown.
|
||||
*
|
||||
* A service is considered expired if any of its DNS record is expired.
|
||||
*/
|
||||
@NonNull
|
||||
public Instant getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attribute value for {@code key} as a UTF-8 string. It's the caller who must make sure
|
||||
* that the value of {@code key} is indeed a UTF-8 string. {@code null} will be returned if no
|
||||
|
|
@ -364,6 +385,7 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
out.writeTypedList(textEntries);
|
||||
out.writeInt(interfaceIndex);
|
||||
out.writeParcelable(network, 0);
|
||||
out.writeLong(expirationTime.getEpochSecond());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -377,7 +399,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
+ ", interfaceIndex: " + interfaceIndex
|
||||
+ ", network: " + network
|
||||
+ ", textStrings: " + textStrings
|
||||
+ ", textEntries: " + textEntries;
|
||||
+ ", textEntries: " + textEntries
|
||||
+ ", expirationTime: " + expirationTime;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -496,4 +519,4 @@ public class MdnsServiceInfo implements Parcelable {
|
|||
out.writeByteArray(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.android.server.connectivity.mdns.util.MdnsUtils;
|
|||
import java.io.PrintWriter;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
|
@ -309,6 +310,7 @@ public class MdnsServiceTypeClient {
|
|||
textStrings = response.getTextRecord().getStrings();
|
||||
textEntries = response.getTextRecord().getEntries();
|
||||
}
|
||||
Instant now = Instant.now();
|
||||
// TODO: Throw an error message if response doesn't have Inet6 or Inet4 address.
|
||||
return new MdnsServiceInfo(
|
||||
serviceInstanceName,
|
||||
|
|
@ -321,7 +323,8 @@ public class MdnsServiceTypeClient {
|
|||
textStrings,
|
||||
textEntries,
|
||||
response.getInterfaceIndex(),
|
||||
response.getNetwork());
|
||||
response.getNetwork(),
|
||||
now.plusMillis(response.getMinRemainingTtl(now.toEpochMilli())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
106
tests/unit/java/android/net/nsd/AdvertisingRequestTest.kt
Normal file
106
tests/unit/java/android/net/nsd/AdvertisingRequestTest.kt
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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.net.nsd.AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY
|
||||
import android.net.nsd.NsdManager.PROTOCOL_DNS_SD
|
||||
import android.os.Build
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.testutils.ConnectivityModuleTest
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.parcelingRoundTrip
|
||||
import java.time.Duration
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNull
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
// TODO: move this class to CTS tests when AdvertisingRequest is made public
|
||||
/** Unit tests for {@link AdvertisingRequest}. */
|
||||
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@SmallTest
|
||||
@ConnectivityModuleTest
|
||||
class AdvertisingRequestTest {
|
||||
@Test
|
||||
fun testParcelingIsLossLess() {
|
||||
val info = NsdServiceInfo().apply {
|
||||
serviceType = "_ipp._tcp"
|
||||
}
|
||||
val beforeParcel = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD)
|
||||
.setAdvertisingConfig(NSD_ADVERTISING_UPDATE_ONLY)
|
||||
.setTtl(Duration.ofSeconds(30L))
|
||||
.build()
|
||||
|
||||
val afterParcel = parcelingRoundTrip(beforeParcel)
|
||||
|
||||
assertEquals(beforeParcel.serviceInfo.serviceType, afterParcel.serviceInfo.serviceType)
|
||||
assertEquals(beforeParcel.advertisingConfig, afterParcel.advertisingConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuilder_setNullTtl_success() {
|
||||
val info = NsdServiceInfo().apply {
|
||||
serviceType = "_ipp._tcp"
|
||||
}
|
||||
val request = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD)
|
||||
.setTtl(null)
|
||||
.build()
|
||||
|
||||
assertNull(request.ttl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuilder_setPropertiesSuccess() {
|
||||
val info = NsdServiceInfo().apply {
|
||||
serviceType = "_ipp._tcp"
|
||||
}
|
||||
val request = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD)
|
||||
.setAdvertisingConfig(NSD_ADVERTISING_UPDATE_ONLY)
|
||||
.setTtl(Duration.ofSeconds(100L))
|
||||
.build()
|
||||
|
||||
assertEquals("_ipp._tcp", request.serviceInfo.serviceType)
|
||||
assertEquals(PROTOCOL_DNS_SD, request.protocolType)
|
||||
assertEquals(NSD_ADVERTISING_UPDATE_ONLY, request.advertisingConfig)
|
||||
assertEquals(Duration.ofSeconds(100L), request.ttl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEquality() {
|
||||
val info = NsdServiceInfo().apply {
|
||||
serviceType = "_ipp._tcp"
|
||||
}
|
||||
val request1 = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD).build()
|
||||
val request2 = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD).build()
|
||||
val request3 = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD)
|
||||
.setAdvertisingConfig(NSD_ADVERTISING_UPDATE_ONLY)
|
||||
.setTtl(Duration.ofSeconds(120L))
|
||||
.build()
|
||||
val request4 = AdvertisingRequest.Builder(info, PROTOCOL_DNS_SD)
|
||||
.setAdvertisingConfig(NSD_ADVERTISING_UPDATE_ONLY)
|
||||
.setTtl(Duration.ofSeconds(120L))
|
||||
.build()
|
||||
|
||||
assertEquals(request1, request2)
|
||||
assertEquals(request3, request4)
|
||||
assertNotEquals(request1, request3)
|
||||
assertNotEquals(request2, request4)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
|
@ -54,6 +55,7 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.time.Duration;
|
||||
|
||||
@DevSdkIgnoreRunner.MonitorThreadLeak
|
||||
@RunWith(DevSdkIgnoreRunner.class)
|
||||
|
|
@ -224,6 +226,23 @@ public class NsdManagerTest {
|
|||
verify(listener, timeout(mTimeoutMs).times(1)).onServiceRegistered(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterServiceWithCustomTtl() throws Exception {
|
||||
final NsdManager manager = mManager;
|
||||
final NsdServiceInfo info = new NsdServiceInfo("another_name2", "another_type2");
|
||||
info.setPort(2203);
|
||||
final AdvertisingRequest request = new AdvertisingRequest.Builder(info, PROTOCOL)
|
||||
.setTtl(Duration.ofSeconds(30)).build();
|
||||
final NsdManager.RegistrationListener listener = mock(
|
||||
NsdManager.RegistrationListener.class);
|
||||
|
||||
manager.registerService(request, Runnable::run, listener);
|
||||
|
||||
AdvertisingRequest capturedRequest = getAdvertisingRequest(
|
||||
req -> verify(mServiceConn).registerService(anyInt(), req.capture()));
|
||||
assertEquals(request, capturedRequest);
|
||||
}
|
||||
|
||||
private void doTestRegisterService() throws Exception {
|
||||
NsdManager manager = mManager;
|
||||
|
||||
|
|
@ -501,4 +520,12 @@ public class NsdManagerTest {
|
|||
verifier.accept(captor);
|
||||
return captor.getValue();
|
||||
}
|
||||
|
||||
AdvertisingRequest getAdvertisingRequest(
|
||||
ThrowingConsumer<ArgumentCaptor<AdvertisingRequest>> verifier) throws Exception {
|
||||
final ArgumentCaptor<AdvertisingRequest> captor =
|
||||
ArgumentCaptor.forClass(AdvertisingRequest.class);
|
||||
verifier.accept(captor);
|
||||
return captor.getValue();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ 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.AdvertisingRequest;
|
||||
import android.net.nsd.INsdManagerCallback;
|
||||
import android.net.nsd.INsdServiceConnector;
|
||||
import android.net.nsd.MDnsManager;
|
||||
|
|
@ -101,6 +102,7 @@ import android.os.HandlerThread;
|
|||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Pair;
|
||||
|
||||
|
|
@ -110,6 +112,7 @@ import androidx.test.filters.SmallTest;
|
|||
import com.android.metrics.NetworkNsdReportedMetrics;
|
||||
import com.android.server.NsdService.Dependencies;
|
||||
import com.android.server.connectivity.mdns.MdnsAdvertiser;
|
||||
import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
|
||||
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
|
||||
import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
|
||||
import com.android.server.connectivity.mdns.MdnsSearchOptions;
|
||||
|
|
@ -137,6 +140,8 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -971,7 +976,8 @@ public class NsdServiceTest {
|
|||
List.of() /* textStrings */,
|
||||
List.of() /* textEntries */,
|
||||
1234,
|
||||
network);
|
||||
network,
|
||||
Instant.MAX /* expirationTime */);
|
||||
|
||||
// Callbacks for query sent.
|
||||
listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
|
||||
|
|
@ -1001,7 +1007,8 @@ public class NsdServiceTest {
|
|||
List.of() /* textStrings */,
|
||||
List.of() /* textEntries */,
|
||||
1234,
|
||||
network);
|
||||
network,
|
||||
Instant.MAX /* expirationTime */);
|
||||
|
||||
// Verify onServiceUpdated callback.
|
||||
listener.onServiceUpdated(updatedServiceInfo);
|
||||
|
|
@ -1133,7 +1140,8 @@ public class NsdServiceTest {
|
|||
List.of(), /* textStrings */
|
||||
List.of(), /* textEntries */
|
||||
1234, /* interfaceIndex */
|
||||
network);
|
||||
network,
|
||||
Instant.MAX /* expirationTime */);
|
||||
|
||||
// Verify onServiceNameDiscovered callback
|
||||
listener.onServiceNameDiscovered(foundInfo, false /* isServiceFromCache */);
|
||||
|
|
@ -1154,7 +1162,8 @@ public class NsdServiceTest {
|
|||
null, /* textStrings */
|
||||
null, /* textEntries */
|
||||
1234, /* interfaceIndex */
|
||||
network);
|
||||
network,
|
||||
Instant.MAX /* expirationTime */);
|
||||
// Verify onServiceNameRemoved callback
|
||||
listener.onServiceNameRemoved(removedInfo);
|
||||
verify(discListener, timeout(TIMEOUT_MS)).onServiceLost(argThat(info ->
|
||||
|
|
@ -1276,7 +1285,8 @@ public class NsdServiceTest {
|
|||
List.of(MdnsServiceInfo.TextEntry.fromBytes(new byte[]{
|
||||
'k', 'e', 'y', '=', (byte) 0xFF, (byte) 0xFE})) /* textEntries */,
|
||||
1234,
|
||||
network);
|
||||
network,
|
||||
Instant.ofEpochSecond(1000_000L) /* expirationTime */);
|
||||
|
||||
// Verify onServiceFound callback
|
||||
doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
|
||||
|
|
@ -1301,6 +1311,7 @@ public class NsdServiceTest {
|
|||
assertTrue(info.getHostAddresses().stream().anyMatch(
|
||||
address -> address.equals(parseNumericAddress("2001:db8::2"))));
|
||||
assertEquals(network, info.getNetwork());
|
||||
assertEquals(Instant.ofEpochSecond(1000_000L), info.getExpirationTime());
|
||||
|
||||
// Verify the listener has been unregistered.
|
||||
verify(mDiscoveryManager, timeout(TIMEOUT_MS))
|
||||
|
|
@ -1517,6 +1528,82 @@ public class NsdServiceTest {
|
|||
false /* isLegacy */, regId, 10L /* durationMs */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvertiseCustomTtl_validTtl_success() {
|
||||
runValidTtlAdvertisingTest(30L);
|
||||
runValidTtlAdvertisingTest(10 * 3600L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvertiseCustomTtl_ttlSmallerThan30SecondsButClientIsSystemServer_success() {
|
||||
when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID);
|
||||
|
||||
runValidTtlAdvertisingTest(29L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvertiseCustomTtl_ttlLargerThan10HoursButClientIsSystemServer_success() {
|
||||
when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID);
|
||||
|
||||
runValidTtlAdvertisingTest(10 * 3600L + 1);
|
||||
runValidTtlAdvertisingTest(0xffffffffL);
|
||||
}
|
||||
|
||||
private void runValidTtlAdvertisingTest(long validTtlSeconds) {
|
||||
setMdnsAdvertiserEnabled();
|
||||
|
||||
final NsdManager client = connectClient(mService);
|
||||
final RegistrationListener regListener = mock(RegistrationListener.class);
|
||||
final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
|
||||
ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
|
||||
verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
|
||||
|
||||
final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE);
|
||||
regInfo.setPort(1234);
|
||||
final AdvertisingRequest request =
|
||||
new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD)
|
||||
.setTtl(Duration.ofSeconds(validTtlSeconds)).build();
|
||||
|
||||
client.registerService(request, Runnable::run, regListener);
|
||||
waitForIdle();
|
||||
|
||||
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
final MdnsAdvertisingOptions expectedAdverstingOptions =
|
||||
MdnsAdvertisingOptions.newBuilder().setTtl(request.getTtl()).build();
|
||||
verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), any(),
|
||||
eq(expectedAdverstingOptions), anyInt());
|
||||
|
||||
// Verify onServiceRegistered callback
|
||||
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
|
||||
final int regId = idCaptor.getValue();
|
||||
cb.onRegisterServiceSucceeded(regId, regInfo);
|
||||
|
||||
verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(
|
||||
argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvertiseCustomTtl_invalidTtl_FailsWithBadParameters() {
|
||||
setMdnsAdvertiserEnabled();
|
||||
final long invalidTtlSeconds = 29L;
|
||||
final NsdManager client = connectClient(mService);
|
||||
final RegistrationListener regListener = mock(RegistrationListener.class);
|
||||
final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
|
||||
ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
|
||||
verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
|
||||
|
||||
final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE);
|
||||
regInfo.setPort(1234);
|
||||
final AdvertisingRequest request =
|
||||
new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD)
|
||||
.setTtl(Duration.ofSeconds(invalidTtlSeconds)).build();
|
||||
client.registerService(request, Runnable::run, regListener);
|
||||
waitForIdle();
|
||||
|
||||
verify(regListener, timeout(TIMEOUT_MS))
|
||||
.onRegistrationFailed(any(), eq(FAILURE_BAD_PARAMETERS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopServiceResolutionWithMdnsDiscoveryManager() {
|
||||
setMdnsDiscoveryManagerEnabled();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
|||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.waitForIdle
|
||||
import java.net.NetworkInterface
|
||||
import java.time.Duration
|
||||
import java.util.Objects
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
@ -313,9 +314,9 @@ class MdnsAdvertiserTest {
|
|||
eq(thread.looper), any(), intAdvCbCaptor2.capture(), eq(TEST_HOSTNAME), any(), any()
|
||||
)
|
||||
verify(mockInterfaceAdvertiser1).addService(
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE))
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE), any())
|
||||
verify(mockInterfaceAdvertiser2).addService(
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE))
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE), any())
|
||||
|
||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||
postSync { intAdvCbCaptor1.value.onServiceProbingSucceeded(
|
||||
|
|
@ -489,15 +490,15 @@ class MdnsAdvertiserTest {
|
|||
eq(thread.looper), any(), intAdvCbCaptor.capture(), eq(TEST_HOSTNAME), any(), any()
|
||||
)
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(SERVICE_1) })
|
||||
argThat { it.matches(SERVICE_1) }, any())
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_2),
|
||||
argThat { it.matches(expectedRenamed) })
|
||||
argThat { it.matches(expectedRenamed) }, any())
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_1),
|
||||
argThat { it.matches(LONG_SERVICE_1) })
|
||||
argThat { it.matches(LONG_SERVICE_1) }, any())
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_2),
|
||||
argThat { it.matches(expectedLongRenamed) })
|
||||
argThat { it.matches(expectedLongRenamed) }, any())
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(CASE_INSENSITIVE_TEST_SERVICE_ID),
|
||||
argThat { it.matches(expectedCaseInsensitiveRenamed) })
|
||||
argThat { it.matches(expectedCaseInsensitiveRenamed) }, any())
|
||||
|
||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||
postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
|
||||
|
|
@ -532,7 +533,7 @@ class MdnsAdvertiserTest {
|
|||
postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_1, mockSocket1, listOf(TEST_LINKADDR)) }
|
||||
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) })
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) }, any())
|
||||
|
||||
val updateOptions = MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(true).build()
|
||||
|
||||
|
|
@ -554,7 +555,24 @@ class MdnsAdvertiserTest {
|
|||
// Newly created MdnsInterfaceAdvertiser will get addService() call.
|
||||
postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_2, mockSocket2, listOf(TEST_LINKADDR2)) }
|
||||
verify(mockInterfaceAdvertiser2).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE_SUBTYPE) })
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE_SUBTYPE) }, any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddOrUpdateService_customTtl_registeredSuccess() {
|
||||
val advertiser = MdnsAdvertiser(
|
||||
thread.looper, socketProvider, cb, mockDeps, sharedlog, flags, context)
|
||||
val updateOptions =
|
||||
MdnsAdvertisingOptions.newBuilder().setTtl(Duration.ofSeconds(30)).build()
|
||||
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE,
|
||||
updateOptions, TEST_CLIENT_UID_1) }
|
||||
|
||||
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(null), socketCbCaptor.capture())
|
||||
val socketCb = socketCbCaptor.value
|
||||
postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_1, mockSocket1, listOf(TEST_LINKADDR)) }
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1), any(), eq(updateOptions))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ class MdnsInterfaceAdvertiserTest {
|
|||
knownServices.add(inv.getArgument(0))
|
||||
|
||||
-1
|
||||
}.`when`(repository).addService(anyInt(), any())
|
||||
}.`when`(repository).addService(anyInt(), any(), any())
|
||||
doAnswer { inv ->
|
||||
knownServices.remove(inv.getArgument(0))
|
||||
null
|
||||
|
|
@ -403,9 +403,10 @@ class MdnsInterfaceAdvertiserTest {
|
|||
@Test
|
||||
fun testReplaceExitingService() {
|
||||
doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
advertiser.addService(TEST_SERVICE_ID_DUPLICATE, TEST_SERVICE_1_SUBTYPE)
|
||||
verify(repository).addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
advertiser.addService(TEST_SERVICE_ID_DUPLICATE, TEST_SERVICE_1_SUBTYPE,
|
||||
MdnsAdvertisingOptions.getDefaultOptions())
|
||||
verify(repository).addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
verify(announcer).stop(TEST_SERVICE_ID_DUPLICATE)
|
||||
verify(prober).startProbing(any())
|
||||
}
|
||||
|
|
@ -413,7 +414,7 @@ class MdnsInterfaceAdvertiserTest {
|
|||
@Test
|
||||
fun testUpdateExistingService() {
|
||||
doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
val subTypes = setOf("_sub")
|
||||
advertiser.updateService(TEST_SERVICE_ID_DUPLICATE, subTypes)
|
||||
verify(repository).updateService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
|
|
@ -427,8 +428,8 @@ class MdnsInterfaceAdvertiserTest {
|
|||
doReturn(serviceId).`when`(testProbingInfo).serviceId
|
||||
doReturn(testProbingInfo).`when`(repository).setServiceProbing(serviceId)
|
||||
|
||||
advertiser.addService(serviceId, serviceInfo)
|
||||
verify(repository).addService(serviceId, serviceInfo)
|
||||
advertiser.addService(serviceId, serviceInfo, MdnsAdvertisingOptions.getDefaultOptions())
|
||||
verify(repository).addService(serviceId, serviceInfo, null /* ttl */)
|
||||
verify(prober).startProbing(testProbingInfo)
|
||||
|
||||
// Simulate probing success: continues to announcing
|
||||
|
|
|
|||
|
|
@ -145,7 +145,8 @@ class MdnsRecordRepositoryTest {
|
|||
fun testAddServiceAndProbe() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
assertEquals(0, repository.servicesCount)
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
|
||||
assertEquals(-1,
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -180,10 +181,10 @@ class MdnsRecordRepositoryTest {
|
|||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
assertFailsWith(NameConflictException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* ttl */)
|
||||
}
|
||||
assertFailsWith(NameConflictException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3)
|
||||
repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3, null /* ttl */)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,9 +225,9 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testInvalidReuseOfServiceId() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2, null /* ttl */)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +236,7 @@ class MdnsRecordRepositoryTest {
|
|||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
assertFalse(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -327,7 +328,7 @@ class MdnsRecordRepositoryTest {
|
|||
repository.exitService(TEST_SERVICE_ID_1)
|
||||
|
||||
assertEquals(TEST_SERVICE_ID_1,
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1))
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* ttl */))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
repository.removeService(TEST_SERVICE_ID_2)
|
||||
|
|
@ -824,7 +825,7 @@ class MdnsRecordRepositoryTest {
|
|||
repository.initWithService(
|
||||
TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, subtypes = setOf(),
|
||||
listOf(LinkAddress(parseNumericAddress("192.0.2.111"), 24)))
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
val src = InetSocketAddress(parseNumericAddress("fe80::123"), 5353)
|
||||
|
||||
val query = makeQuery(TYPE_AAAA to TEST_CUSTOM_HOST_1_NAME)
|
||||
|
|
@ -890,7 +891,8 @@ class MdnsRecordRepositoryTest {
|
|||
repository.initWithService(
|
||||
TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, subtypes = setOf(),
|
||||
listOf(LinkAddress(parseNumericAddress("192.0.2.111"), 24)))
|
||||
repository.addService(TEST_SERVICE_CUSTOM_HOST_ID_1, TEST_SERVICE_CUSTOM_HOST_1)
|
||||
repository.addService(
|
||||
TEST_SERVICE_CUSTOM_HOST_ID_1, TEST_SERVICE_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.removeService(TEST_CUSTOM_HOST_ID_1)
|
||||
repository.removeService(TEST_SERVICE_CUSTOM_HOST_ID_1)
|
||||
|
||||
|
|
@ -989,8 +991,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0 /* flags */,
|
||||
|
|
@ -1020,8 +1022,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServicesCaseInsensitive() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0 /* flags */,
|
||||
|
|
@ -1050,8 +1052,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_customHosts_differentAddresses() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0, /* flags */
|
||||
|
|
@ -1074,8 +1076,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_customHosts_moreAddressesThanUs_conflict() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0, /* flags */
|
||||
|
|
@ -1101,8 +1103,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_customHostsReplyHasFewerAddressesThanUs_noConflict() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0, /* flags */
|
||||
|
|
@ -1122,8 +1124,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_customHostsReplyHasIdenticalHosts_noConflict() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0, /* flags */
|
||||
|
|
@ -1147,8 +1149,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_customHostsCaseInsensitiveReplyHasIdenticalHosts_noConflict() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
|
||||
repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0, /* flags */
|
||||
|
|
@ -1171,8 +1173,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_IdenticalService() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* ttl */)
|
||||
|
||||
val otherTtlMillis = 1234L
|
||||
val packet = MdnsPacket(
|
||||
|
|
@ -1200,8 +1202,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServicesCaseInsensitive_IdenticalService() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* ttl */)
|
||||
|
||||
val otherTtlMillis = 1234L
|
||||
val packet = MdnsPacket(
|
||||
|
|
@ -1256,7 +1258,8 @@ class MdnsRecordRepositoryTest {
|
|||
makeFlags(includeInetAddressesInProbing = true))
|
||||
repository.updateAddresses(TEST_ADDRESSES)
|
||||
assertEquals(0, repository.servicesCount)
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
|
||||
assertEquals(-1,
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -1689,7 +1692,7 @@ private fun MdnsRecordRepository.addServiceAndFinishProbing(
|
|||
serviceId: Int,
|
||||
serviceInfo: NsdServiceInfo
|
||||
): AnnouncementInfo {
|
||||
addService(serviceId, serviceInfo)
|
||||
addService(serviceId, serviceInfo, null /* ttl */)
|
||||
val probingInfo = setServiceProbing(serviceId)
|
||||
assertNotNull(probingInfo)
|
||||
return onProbingSucceeded(probingInfo)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.android.testutils.DevSdkIgnoreRunner;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -202,7 +203,8 @@ public class MdnsServiceInfoTest {
|
|||
List.of(),
|
||||
/* textEntries= */ null,
|
||||
/* interfaceIndex= */ 20,
|
||||
network);
|
||||
network,
|
||||
Instant.MAX /* expirationTime */);
|
||||
|
||||
assertEquals(network, info2.getNetwork());
|
||||
}
|
||||
|
|
@ -225,7 +227,8 @@ public class MdnsServiceInfoTest {
|
|||
MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max"),
|
||||
MdnsServiceInfo.TextEntry.fromString("test=")),
|
||||
20 /* interfaceIndex */,
|
||||
new Network(123));
|
||||
new Network(123),
|
||||
Instant.MAX /* expirationTime */);
|
||||
|
||||
beforeParcel.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue