Report more advertising metrics data

Report more advertising metrics data below when the service is
unregistered.
- Replied request count (sum across interfaces)
- Sent packet count (including announcements and probes)
- Number of conflicts during probing
- Nubmer of conflicts after probing

Bug: 287546772
Test: atest FrameworksNetTestCases NsdManagerTest
Merged-In: I50c54a35dc523422e3a7302c059bbbc38eac5631
Change-Id: I50c54a35dc523422e3a7302c059bbbc38eac5631
This commit is contained in:
Paul Hu
2023-07-14 16:38:25 +08:00
parent a337d95cc4
commit 043bcd4537
15 changed files with 333 additions and 38 deletions

View File

@@ -24,16 +24,22 @@ import android.stats.connectivity.NsdEventType;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ConnectivityStatsLog; import com.android.server.ConnectivityStatsLog;
import java.util.Random;
/** /**
* Class to record the NetworkNsdReported into statsd. Each client should create this class to * Class to record the NetworkNsdReported into statsd. Each client should create this class to
* report its data. * report its data.
*/ */
public class NetworkNsdReportedMetrics { public class NetworkNsdReportedMetrics {
// The upper bound for the random number used in metrics data sampling determines the possible
// sample rate.
private static final int RANDOM_NUMBER_UPPER_BOUND = 1000;
// Whether this client is using legacy backend. // Whether this client is using legacy backend.
private final boolean mIsLegacy; private final boolean mIsLegacy;
// The client id. // The client id.
private final int mClientId; private final int mClientId;
private final Dependencies mDependencies; private final Dependencies mDependencies;
private final Random mRandom;
public NetworkNsdReportedMetrics(boolean isLegacy, int clientId) { public NetworkNsdReportedMetrics(boolean isLegacy, int clientId) {
this(isLegacy, clientId, new Dependencies()); this(isLegacy, clientId, new Dependencies());
@@ -44,6 +50,7 @@ public class NetworkNsdReportedMetrics {
mIsLegacy = isLegacy; mIsLegacy = isLegacy;
mClientId = clientId; mClientId = clientId;
mDependencies = dependencies; mDependencies = dependencies;
mRandom = dependencies.makeRandomGenerator();
} }
/** /**
@@ -67,7 +74,18 @@ public class NetworkNsdReportedMetrics {
event.getFoundCallbackCount(), event.getFoundCallbackCount(),
event.getLostCallbackCount(), event.getLostCallbackCount(),
event.getRepliedRequestsCount(), event.getRepliedRequestsCount(),
event.getSentQueryCount()); event.getSentQueryCount(),
event.getSentPacketCount(),
event.getConflictDuringProbingCount(),
event.getConflictAfterProbingCount(),
event.getRandomNumber());
}
/**
* @see Random
*/
public Random makeRandomGenerator() {
return new Random();
} }
} }
@@ -75,6 +93,7 @@ public class NetworkNsdReportedMetrics {
final Builder builder = NetworkNsdReported.newBuilder(); final Builder builder = NetworkNsdReported.newBuilder();
builder.setIsLegacy(mIsLegacy); builder.setIsLegacy(mIsLegacy);
builder.setClientId(mClientId); builder.setClientId(mClientId);
builder.setRandomNumber(mRandom.nextInt(RANDOM_NUMBER_UPPER_BOUND));
return builder; return builder;
} }
@@ -113,14 +132,23 @@ public class NetworkNsdReportedMetrics {
* *
* @param transactionId The transaction id of service registration. * @param transactionId The transaction id of service registration.
* @param durationMs The duration of service stayed registered. * @param durationMs The duration of service stayed registered.
* @param repliedRequestsCount The replied request count of this service before unregistered it.
* @param sentPacketCount The total sent packet count of this service before unregistered it.
* @param conflictDuringProbingCount The number of conflict during probing.
* @param conflictAfterProbingCount The number of conflict after probing.
*/ */
public void reportServiceUnregistration(int transactionId, long durationMs) { public void reportServiceUnregistration(int transactionId, long durationMs,
int repliedRequestsCount, int sentPacketCount, int conflictDuringProbingCount,
int conflictAfterProbingCount) {
final Builder builder = makeReportedBuilder(); final Builder builder = makeReportedBuilder();
builder.setTransactionId(transactionId); builder.setTransactionId(transactionId);
builder.setType(NsdEventType.NET_REGISTER); builder.setType(NsdEventType.NET_REGISTER);
builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED); builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED);
builder.setEventDurationMillisec(durationMs); builder.setEventDurationMillisec(durationMs);
// TODO: Report repliedRequestsCount builder.setRepliedRequestsCount(repliedRequestsCount);
builder.setSentPacketCount(sentPacketCount);
builder.setConflictDuringProbingCount(conflictDuringProbingCount);
builder.setConflictAfterProbingCount(conflictAfterProbingCount);
mDependencies.statsWrite(builder.build()); mDependencies.statsWrite(builder.build());
} }

View File

@@ -29,6 +29,8 @@ import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE; import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH; import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock; import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
@@ -992,14 +994,20 @@ public class NsdService extends INsdManager.Stub {
// instead of looking at the flag value. // instead of looking at the flag value.
final long stopTimeMs = mClock.elapsedRealtime(); final long stopTimeMs = mClock.elapsedRealtime();
if (request instanceof AdvertiserClientRequest) { if (request instanceof AdvertiserClientRequest) {
final AdvertiserMetrics metrics =
mAdvertiser.getAdvertiserMetrics(transactionId);
mAdvertiser.removeService(transactionId); mAdvertiser.removeService(transactionId);
clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId, clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId,
request.calculateRequestDurationMs(stopTimeMs)); request.calculateRequestDurationMs(stopTimeMs), metrics);
} else { } else {
if (unregisterService(transactionId)) { if (unregisterService(transactionId)) {
clientInfo.onUnregisterServiceSucceeded(clientRequestId, clientInfo.onUnregisterServiceSucceeded(clientRequestId,
transactionId, transactionId,
request.calculateRequestDurationMs(stopTimeMs)); request.calculateRequestDurationMs(stopTimeMs),
new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
NO_PACKET /* sentPacketCount */,
0 /* conflictDuringProbingCount */,
0 /* conflictAfterProbingCount */));
} else { } else {
clientInfo.onUnregisterServiceFailed( clientInfo.onUnregisterServiceFailed(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
@@ -2496,9 +2504,14 @@ public class NsdService extends INsdManager.Stub {
} }
if (request instanceof AdvertiserClientRequest) { if (request instanceof AdvertiserClientRequest) {
final AdvertiserMetrics metrics =
mAdvertiser.getAdvertiserMetrics(transactionId);
mAdvertiser.removeService(transactionId); mAdvertiser.removeService(transactionId);
mMetrics.reportServiceUnregistration(transactionId, mMetrics.reportServiceUnregistration(transactionId,
request.calculateRequestDurationMs(mClock.elapsedRealtime())); request.calculateRequestDurationMs(mClock.elapsedRealtime()),
metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
metrics.mConflictDuringProbingCount,
metrics.mConflictAfterProbingCount);
continue; continue;
} }
@@ -2524,7 +2537,11 @@ public class NsdService extends INsdManager.Stub {
case NsdManager.REGISTER_SERVICE: case NsdManager.REGISTER_SERVICE:
unregisterService(transactionId); unregisterService(transactionId);
mMetrics.reportServiceUnregistration(transactionId, mMetrics.reportServiceUnregistration(transactionId,
request.calculateRequestDurationMs(mClock.elapsedRealtime())); request.calculateRequestDurationMs(mClock.elapsedRealtime()),
NO_PACKET /* repliedRequestsCount */,
NO_PACKET /* sentPacketCount */,
0 /* conflictDuringProbingCount */,
0 /* conflictAfterProbingCount */);
break; break;
default: default:
break; break;
@@ -2663,8 +2680,11 @@ public class NsdService extends INsdManager.Stub {
} }
} }
void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs) { void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs,
mMetrics.reportServiceUnregistration(transactionId, durationMs); AdvertiserMetrics metrics) {
mMetrics.reportServiceUnregistration(transactionId, durationMs,
metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
try { try {
mCb.onUnregisterServiceSucceeded(listenerKey); mCb.onUnregisterServiceSucceeded(listenerKey);
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@@ -16,6 +16,7 @@
package com.android.server.connectivity.mdns; package com.android.server.connectivity.mdns;
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH; import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -180,6 +181,7 @@ public class MdnsAdvertiser {
// (with the old, conflicting, actually not used name as argument... The new // (with the old, conflicting, actually not used name as argument... The new
// implementation will send callbacks with the new name). // implementation will send callbacks with the new name).
registration.mNotifiedRegistrationSuccess = false; registration.mNotifiedRegistrationSuccess = false;
registration.mConflictAfterProbingCount++;
// The service was done probing, just reset it to probing state (RFC6762 9.) // The service was done probing, just reset it to probing state (RFC6762 9.)
forAllAdvertisers(a -> { forAllAdvertisers(a -> {
@@ -195,6 +197,7 @@ public class MdnsAdvertiser {
registration.updateForConflict( registration.updateForConflict(
registration.makeNewServiceInfoForConflict(1 /* renameCount */), registration.makeNewServiceInfoForConflict(1 /* renameCount */),
1 /* renameCount */); 1 /* renameCount */);
registration.mConflictDuringProbingCount++;
// Keep renaming if the new name conflicts in local registrations // Keep renaming if the new name conflicts in local registrations
updateRegistrationUntilNoConflict((net, adv) -> adv.hasRegistration(registration), updateRegistrationUntilNoConflict((net, adv) -> adv.hasRegistration(registration),
@@ -360,6 +363,22 @@ public class MdnsAdvertiser {
} }
} }
int getServiceRepliedRequestsCount(int id) {
int repliedRequestsCount = NO_PACKET;
for (int i = 0; i < mAdvertisers.size(); i++) {
repliedRequestsCount += mAdvertisers.valueAt(i).getServiceRepliedRequestsCount(id);
}
return repliedRequestsCount;
}
int getSentPacketCount(int id) {
int sentPacketCount = NO_PACKET;
for (int i = 0; i < mAdvertisers.size(); i++) {
sentPacketCount += mAdvertisers.valueAt(i).getSentPacketCount(id);
}
return sentPacketCount;
}
@Override @Override
public void onSocketCreated(@NonNull SocketKey socketKey, public void onSocketCreated(@NonNull SocketKey socketKey,
@NonNull MdnsInterfaceSocket socket, @NonNull MdnsInterfaceSocket socket,
@@ -444,6 +463,8 @@ public class MdnsAdvertiser {
private NsdServiceInfo mServiceInfo; private NsdServiceInfo mServiceInfo;
@Nullable @Nullable
private final String mSubtype; private final String mSubtype;
int mConflictDuringProbingCount;
int mConflictAfterProbingCount;
private Registration(@NonNull NsdServiceInfo serviceInfo, @Nullable String subtype) { private Registration(@NonNull NsdServiceInfo serviceInfo, @Nullable String subtype) {
this.mOriginalName = serviceInfo.getServiceName(); this.mOriginalName = serviceInfo.getServiceName();
@@ -555,6 +576,24 @@ public class MdnsAdvertiser {
@NonNull OffloadServiceInfo offloadServiceInfo); @NonNull OffloadServiceInfo offloadServiceInfo);
} }
/**
* Data class of avdverting metrics.
*/
public static class AdvertiserMetrics {
public final int mRepliedRequestsCount;
public final int mSentPacketCount;
public final int mConflictDuringProbingCount;
public final int mConflictAfterProbingCount;
public AdvertiserMetrics(int repliedRequestsCount, int sentPacketCount,
int conflictDuringProbingCount, int conflictAfterProbingCount) {
mRepliedRequestsCount = repliedRequestsCount;
mSentPacketCount = sentPacketCount;
mConflictDuringProbingCount = conflictDuringProbingCount;
mConflictAfterProbingCount = conflictAfterProbingCount;
}
}
public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider, public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
@NonNull AdvertiserCallback cb, @NonNull SharedLog sharedLog) { @NonNull AdvertiserCallback cb, @NonNull SharedLog sharedLog) {
this(looper, socketProvider, cb, new Dependencies(), sharedLog); this(looper, socketProvider, cb, new Dependencies(), sharedLog);
@@ -637,6 +676,34 @@ public class MdnsAdvertiser {
} }
} }
/**
* Get advertising metrics.
*
* @param id ID used when registering.
* @return The advertising metrics includes replied requests count, send packet count, conflict
* count during/after probing.
*/
public AdvertiserMetrics getAdvertiserMetrics(int id) {
checkThread();
final Registration registration = mRegistrations.get(id);
if (registration == null) {
return new AdvertiserMetrics(
NO_PACKET /* repliedRequestsCount */,
NO_PACKET /* sentPacketCount */,
0 /* conflictDuringProbingCount */,
0 /* conflictAfterProbingCount */);
}
int repliedRequestsCount = NO_PACKET;
int sentPacketCount = NO_PACKET;
for (int i = 0; i < mAdvertiserRequests.size(); i++) {
repliedRequestsCount +=
mAdvertiserRequests.valueAt(i).getServiceRepliedRequestsCount(id);
sentPacketCount += mAdvertiserRequests.valueAt(i).getSentPacketCount(id);
}
return new AdvertiserMetrics(repliedRequestsCount, sentPacketCount,
registration.mConflictDuringProbingCount, registration.mConflictAfterProbingCount);
}
private static <K, V> boolean any(@NonNull ArrayMap<K, V> map, private static <K, V> boolean any(@NonNull ArrayMap<K, V> map,
@NonNull BiPredicate<K, V> predicate) { @NonNull BiPredicate<K, V> predicate) {
for (int i = 0; i < map.size(); i++) { for (int i = 0; i < map.size(); i++) {

View File

@@ -37,6 +37,7 @@ public final class MdnsConstants {
public static final int FLAG_TRUNCATED = 0x0200; public static final int FLAG_TRUNCATED = 0x0200;
public static final int QCLASS_INTERNET = 0x0001; public static final int QCLASS_INTERNET = 0x0001;
public static final int QCLASS_UNICAST = 0x8000; public static final int QCLASS_UNICAST = 0x8000;
public static final int NO_PACKET = 0;
public static final String SUBTYPE_LABEL = "_sub"; public static final String SUBTYPE_LABEL = "_sub";
public static final String SUBTYPE_PREFIX = "_"; public static final String SUBTYPE_PREFIX = "_";
private static final String MDNS_IPV4_HOST_ADDRESS = "224.0.0.251"; private static final String MDNS_IPV4_HOST_ADDRESS = "224.0.0.251";

View File

@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns; package com.android.server.connectivity.mdns;
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.net.LinkAddress; import android.net.LinkAddress;
@@ -93,8 +95,11 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
/** /**
* Callbacks from {@link MdnsProber}. * Callbacks from {@link MdnsProber}.
*/ */
private class ProbingCallback implements private class ProbingCallback implements PacketRepeaterCallback<MdnsProber.ProbingInfo> {
PacketRepeaterCallback<MdnsProber.ProbingInfo> { @Override
public void onSent(int index, @NonNull MdnsProber.ProbingInfo info, int sentPacketCount) {
mRecordRepository.onProbingSent(info.getServiceId(), sentPacketCount);
}
@Override @Override
public void onFinished(MdnsProber.ProbingInfo info) { public void onFinished(MdnsProber.ProbingInfo info) {
final MdnsAnnouncer.AnnouncementInfo announcementInfo; final MdnsAnnouncer.AnnouncementInfo announcementInfo;
@@ -118,8 +123,8 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
*/ */
private class AnnouncingCallback implements PacketRepeaterCallback<BaseAnnouncementInfo> { private class AnnouncingCallback implements PacketRepeaterCallback<BaseAnnouncementInfo> {
@Override @Override
public void onSent(int index, @NonNull BaseAnnouncementInfo info) { public void onSent(int index, @NonNull BaseAnnouncementInfo info, int sentPacketCount) {
mRecordRepository.onAdvertisementSent(info.getServiceId()); mRecordRepository.onAdvertisementSent(info.getServiceId(), sentPacketCount);
} }
@Override @Override
@@ -259,6 +264,22 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
} }
} }
/**
* Get the replied request count from given service id.
*/
public int getServiceRepliedRequestsCount(int id) {
if (!mRecordRepository.hasActiveService(id)) return NO_PACKET;
return mRecordRepository.getServiceRepliedRequestsCount(id);
}
/**
* Get the total sent packet count from given service id.
*/
public int getSentPacketCount(int id) {
if (!mRecordRepository.hasActiveService(id)) return NO_PACKET;
return mRecordRepository.getSentPacketCount(id);
}
/** /**
* Update interface addresses used to advertise. * Update interface addresses used to advertise.
* *

View File

@@ -59,7 +59,7 @@ public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
/** /**
* Called when a packet was sent. * Called when a packet was sent.
*/ */
default void onSent(int index, @NonNull T info) {} default void onSent(int index, @NonNull T info, int sentPacketCount) {}
/** /**
* Called when the {@link MdnsPacketRepeater} is done sending packets. * Called when the {@link MdnsPacketRepeater} is done sending packets.
@@ -114,9 +114,10 @@ public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
} }
// Send to both v4 and v6 addresses; the reply sender will take care of ignoring the // Send to both v4 and v6 addresses; the reply sender will take care of ignoring the
// send when the socket has not joined the relevant group. // send when the socket has not joined the relevant group.
int sentPacketCount = 0;
for (InetSocketAddress destination : ALL_ADDRS) { for (InetSocketAddress destination : ALL_ADDRS) {
try { try {
mReplySender.sendNow(packet, destination); sentPacketCount += mReplySender.sendNow(packet, destination);
} catch (IOException e) { } catch (IOException e) {
mSharedLog.e("Error sending packet to " + destination, e); mSharedLog.e("Error sending packet to " + destination, e);
} }
@@ -135,7 +136,7 @@ public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
// Call onSent after scheduling the next run, to allow the callback to cancel it // Call onSent after scheduling the next run, to allow the callback to cancel it
if (mCb != null) { if (mCb != null) {
mCb.onSent(index, request); mCb.onSent(index, request, sentPacketCount);
} }
} }
} }

View File

@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns; package com.android.server.connectivity.mdns;
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.TargetApi; import android.annotation.TargetApi;
@@ -174,6 +176,16 @@ public class MdnsRecordRepository {
*/ */
public boolean exiting = false; public boolean exiting = false;
/**
* The replied query packet count of this service.
*/
public int repliedServiceCount = NO_PACKET;
/**
* The sent packet count of this service (including announcements and probes).
*/
public int sentPacketCount = NO_PACKET;
/** /**
* Create a ServiceRegistration for dns-sd service registration (RFC6763). * Create a ServiceRegistration for dns-sd service registration (RFC6763).
* *
@@ -181,7 +193,7 @@ public class MdnsRecordRepository {
* @param serviceInfo Service to advertise * @param serviceInfo Service to advertise
*/ */
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo, ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
@Nullable String subtype) { @Nullable String subtype, int repliedServiceCount, int sentPacketCount) {
this.serviceInfo = serviceInfo; this.serviceInfo = serviceInfo;
this.subtype = subtype; this.subtype = subtype;
@@ -254,6 +266,8 @@ public class MdnsRecordRepository {
true /* sharedName */, true /* probing */)); true /* sharedName */, true /* probing */));
this.allRecords = Collections.unmodifiableList(allRecords); this.allRecords = Collections.unmodifiableList(allRecords);
this.repliedServiceCount = repliedServiceCount;
this.sentPacketCount = sentPacketCount;
} }
void setProbing(boolean probing) { void setProbing(boolean probing) {
@@ -316,7 +330,8 @@ public class MdnsRecordRepository {
} }
final ServiceRegistration registration = new ServiceRegistration( final ServiceRegistration registration = new ServiceRegistration(
mDeviceHostname, serviceInfo, subtype); mDeviceHostname, serviceInfo, subtype, NO_PACKET /* repliedServiceCount */,
NO_PACKET /* sentPacketCount */);
mServices.put(serviceId, registration); mServices.put(serviceId, registration);
// Remove existing exiting service // Remove existing exiting service
@@ -405,6 +420,24 @@ public class MdnsRecordRepository {
return mServices.size(); return mServices.size();
} }
/**
* @return The replied request count of the service.
*/
public int getServiceRepliedRequestsCount(int id) {
final ServiceRegistration service = mServices.get(id);
if (service == null) return NO_PACKET;
return service.repliedServiceCount;
}
/**
* @return The total sent packet count of the service.
*/
public int getSentPacketCount(int id) {
final ServiceRegistration service = mServices.get(id);
if (service == null) return NO_PACKET;
return service.sentPacketCount;
}
/** /**
* Remove all services from the repository * Remove all services from the repository
* @return IDs of the removed services * @return IDs of the removed services
@@ -472,9 +505,12 @@ public class MdnsRecordRepository {
for (int i = 0; i < mServices.size(); i++) { for (int i = 0; i < mServices.size(); i++) {
final ServiceRegistration registration = mServices.valueAt(i); final ServiceRegistration registration = mServices.valueAt(i);
if (registration.exiting) continue; if (registration.exiting) continue;
addReplyFromService(question, registration.allRecords, registration.ptrRecords, if (addReplyFromService(question, registration.allRecords, registration.ptrRecords,
registration.srvRecord, registration.txtRecord, replyUnicast, now, registration.srvRecord, registration.txtRecord, replyUnicast, now,
answerInfo, additionalAnswerRecords); answerInfo, additionalAnswerRecords)) {
registration.repliedServiceCount++;
registration.sentPacketCount++;
}
} }
} }
@@ -527,7 +563,7 @@ public class MdnsRecordRepository {
/** /**
* Add answers and additional answers for a question, from a ServiceRegistration. * Add answers and additional answers for a question, from a ServiceRegistration.
*/ */
private void addReplyFromService(@NonNull MdnsRecord question, private boolean addReplyFromService(@NonNull MdnsRecord question,
@NonNull List<RecordInfo<?>> serviceRecords, @NonNull List<RecordInfo<?>> serviceRecords,
@Nullable List<RecordInfo<MdnsPointerRecord>> servicePtrRecords, @Nullable List<RecordInfo<MdnsPointerRecord>> servicePtrRecords,
@Nullable RecordInfo<MdnsServiceRecord> serviceSrvRecord, @Nullable RecordInfo<MdnsServiceRecord> serviceSrvRecord,
@@ -596,7 +632,7 @@ public class MdnsRecordRepository {
} }
// No more records to add if no answer // No more records to add if no answer
if (answerInfo.size() == answersStartIndex) return; if (answerInfo.size() == answersStartIndex) return false;
final List<RecordInfo<?>> additionalAnswerInfo = new ArrayList<>(); final List<RecordInfo<?>> additionalAnswerInfo = new ArrayList<>();
// RFC6763 12.1: if including PTR record, include the SRV and TXT records it names // RFC6763 12.1: if including PTR record, include the SRV and TXT records it names
@@ -626,6 +662,7 @@ public class MdnsRecordRepository {
addNsecRecordsForUniqueNames(additionalAnswerRecords, addNsecRecordsForUniqueNames(additionalAnswerRecords,
answerInfo.listIterator(answersStartIndex), answerInfo.listIterator(answersStartIndex),
additionalAnswerInfo.listIterator()); additionalAnswerInfo.listIterator());
return true;
} }
/** /**
@@ -862,8 +899,8 @@ public class MdnsRecordRepository {
final ServiceRegistration existing = mServices.get(serviceId); final ServiceRegistration existing = mServices.get(serviceId);
if (existing == null) return null; if (existing == null) return null;
final ServiceRegistration newService = new ServiceRegistration( final ServiceRegistration newService = new ServiceRegistration(mDeviceHostname, newInfo,
mDeviceHostname, newInfo, existing.subtype); existing.subtype, existing.repliedServiceCount, existing.sentPacketCount);
mServices.put(serviceId, newService); mServices.put(serviceId, newService);
return makeProbingInfo(serviceId, newService.srvRecord.record); return makeProbingInfo(serviceId, newService.srvRecord.record);
} }
@@ -871,7 +908,7 @@ public class MdnsRecordRepository {
/** /**
* Called when {@link MdnsAdvertiser} sent an advertisement for the given service. * Called when {@link MdnsAdvertiser} sent an advertisement for the given service.
*/ */
public void onAdvertisementSent(int serviceId) { public void onAdvertisementSent(int serviceId, int sentPacketCount) {
final ServiceRegistration registration = mServices.get(serviceId); final ServiceRegistration registration = mServices.get(serviceId);
if (registration == null) return; if (registration == null) return;
@@ -880,8 +917,19 @@ public class MdnsRecordRepository {
record.lastSentTimeMs = now; record.lastSentTimeMs = now;
record.lastAdvertisedTimeMs = now; record.lastAdvertisedTimeMs = now;
} }
registration.sentPacketCount += sentPacketCount;
} }
/**
* Called when {@link MdnsAdvertiser} sent a probing for the given service.
*/
public void onProbingSent(int serviceId, int sentPacketCount) {
final ServiceRegistration registration = mServices.get(serviceId);
if (registration == null) return;
registration.sentPacketCount += sentPacketCount;
}
/** /**
* Compute: * Compute:
* 2001:db8::1 --> 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa * 2001:db8::1 --> 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa

View File

@@ -44,6 +44,9 @@ import java.util.Collections;
public class MdnsReplySender { public class MdnsReplySender {
private static final boolean DBG = MdnsAdvertiser.DBG; private static final boolean DBG = MdnsAdvertiser.DBG;
private static final int MSG_SEND = 1; private static final int MSG_SEND = 1;
private static final int PACKET_NOT_SENT = 0;
private static final int PACKET_SENT = 1;
@NonNull @NonNull
private final MdnsInterfaceSocket mSocket; private final MdnsInterfaceSocket mSocket;
@NonNull @NonNull
@@ -79,16 +82,17 @@ public class MdnsReplySender {
* *
* Must be called on the looper thread used by the {@link MdnsReplySender}. * Must be called on the looper thread used by the {@link MdnsReplySender}.
*/ */
public void sendNow(@NonNull MdnsPacket packet, @NonNull InetSocketAddress destination) public int sendNow(@NonNull MdnsPacket packet, @NonNull InetSocketAddress destination)
throws IOException { throws IOException {
ensureRunningOnHandlerThread(mHandler); ensureRunningOnHandlerThread(mHandler);
if (!((destination.getAddress() instanceof Inet6Address && mSocket.hasJoinedIpv6()) if (!((destination.getAddress() instanceof Inet6Address && mSocket.hasJoinedIpv6())
|| (destination.getAddress() instanceof Inet4Address && mSocket.hasJoinedIpv4()))) { || (destination.getAddress() instanceof Inet4Address && mSocket.hasJoinedIpv4()))) {
// Skip sending if the socket has not joined the v4/v6 group (there was no address) // Skip sending if the socket has not joined the v4/v6 group (there was no address)
return; return PACKET_NOT_SENT;
} }
final byte[] outBuffer = MdnsUtils.createRawDnsPacket(mPacketCreationBuffer, packet); final byte[] outBuffer = MdnsUtils.createRawDnsPacket(mPacketCreationBuffer, packet);
mSocket.send(new DatagramPacket(outBuffer, 0, outBuffer.length, destination)); mSocket.send(new DatagramPacket(outBuffer, 0, outBuffer.length, destination));
return PACKET_SENT;
} }
/** Get the packetCreationBuffer */ /** Get the packetCreationBuffer */

View File

@@ -64,6 +64,18 @@ message NetworkNsdReported {
// Record sent query count before stopped discovery // Record sent query count before stopped discovery
optional int32 sent_query_count = 12; optional int32 sent_query_count = 12;
// Record sent packet count before unregistered service
optional int32 sent_packet_count = 13;
// Record number of conflict during probing
optional int32 conflict_during_probing_count = 14;
// Record number of conflict after probing
optional int32 conflict_after_probing_count = 15;
// The random number between 0 ~ 999 for sampling
optional int32 random_number = 16;
} }
/** /**

View File

@@ -21,12 +21,15 @@ import android.stats.connectivity.MdnsQueryResult
import android.stats.connectivity.NsdEventType import android.stats.connectivity.NsdEventType
import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.DevSdkIgnoreRunner
import java.util.Random
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
@@ -34,6 +37,12 @@ import org.mockito.Mockito.verify
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
class NetworkNsdReportedMetricsTest { class NetworkNsdReportedMetricsTest {
private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java) private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java)
private val random = mock(Random::class.java)
@Before
fun setUp() {
doReturn(random).`when`(deps).makeRandomGenerator()
}
@Test @Test
fun testReportServiceRegistrationSucceeded() { fun testReportServiceRegistrationSucceeded() {
@@ -80,8 +89,13 @@ class NetworkNsdReportedMetricsTest {
val clientId = 99 val clientId = 99
val transactionId = 100 val transactionId = 100
val durationMs = 10L val durationMs = 10L
val repliedRequestsCount = 25
val sentPacketCount = 50
val conflictDuringProbingCount = 2
val conflictAfterProbingCount = 1
val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps) val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
metrics.reportServiceUnregistration(transactionId, durationMs) metrics.reportServiceUnregistration(transactionId, durationMs, repliedRequestsCount,
sentPacketCount, conflictDuringProbingCount, conflictAfterProbingCount)
val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java) val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
verify(deps).statsWrite(eventCaptor.capture()) verify(deps).statsWrite(eventCaptor.capture())
@@ -92,6 +106,10 @@ class NetworkNsdReportedMetricsTest {
assertEquals(NsdEventType.NET_REGISTER, it.type) assertEquals(NsdEventType.NET_REGISTER, it.type)
assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, it.queryResult) assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, it.queryResult)
assertEquals(durationMs, it.eventDurationMillisec) assertEquals(durationMs, it.eventDurationMillisec)
assertEquals(repliedRequestsCount, it.repliedRequestsCount)
assertEquals(sentPacketCount, it.sentPacketCount)
assertEquals(conflictDuringProbingCount, it.conflictDuringProbingCount)
assertEquals(conflictAfterProbingCount, it.conflictAfterProbingCount)
} }
} }

View File

@@ -1222,6 +1222,8 @@ public class NsdServiceTest {
verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue()); verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue());
verify(mAdvertiser, never()).removeService(anyInt()); verify(mAdvertiser, never()).removeService(anyInt());
doReturn(mock(MdnsAdvertiser.AdvertiserMetrics.class))
.when(mAdvertiser).getAdvertiserMetrics(anyInt());
client.unregisterService(regListenerWithFeature); client.unregisterService(regListenerWithFeature);
waitForIdle(); waitForIdle();
verify(mAdvertiser).removeService(serviceIdCaptor.getValue()); verify(mAdvertiser).removeService(serviceIdCaptor.getValue());
@@ -1312,14 +1314,20 @@ public class NsdServiceTest {
new NsdServiceInfo(regInfo.getServiceName(), null)))); new NsdServiceInfo(regInfo.getServiceName(), null))));
verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */); verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */);
final MdnsAdvertiser.AdvertiserMetrics metrics = new MdnsAdvertiser.AdvertiserMetrics(
50 /* repliedRequestCount */, 100 /* sentPacketCount */,
3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime(); doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime();
doReturn(metrics).when(mAdvertiser).getAdvertiserMetrics(regId);
client.unregisterService(regListener); client.unregisterService(regListener);
waitForIdle(); waitForIdle();
verify(mAdvertiser).removeService(idCaptor.getValue()); verify(mAdvertiser).removeService(idCaptor.getValue());
verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered( verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
argThat(info -> matches(info, regInfo))); argThat(info -> matches(info, regInfo)));
verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive(); verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive();
verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */); verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */,
50 /* repliedRequestCount */, 100 /* sentPacketCount */,
3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
} }
@Test @Test

View File

@@ -33,7 +33,10 @@ import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.waitForIdle import com.android.testutils.waitForIdle
import java.net.NetworkInterface import java.net.NetworkInterface
import java.util.Objects import java.util.Objects
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -206,6 +209,18 @@ class MdnsAdvertiserTest {
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) }) verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE)) verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
// Service is conflicted.
postSync { intAdvCbCaptor.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
// Verify the metrics data
doReturn(25).`when`(mockInterfaceAdvertiser1).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(40).`when`(mockInterfaceAdvertiser1).getSentPacketCount(SERVICE_ID_1)
val metrics = postReturn { advertiser.getAdvertiserMetrics(SERVICE_ID_1) }
assertEquals(25, metrics.mRepliedRequestsCount)
assertEquals(40, metrics.mSentPacketCount)
assertEquals(0, metrics.mConflictDuringProbingCount)
assertEquals(1, metrics.mConflictAfterProbingCount)
doReturn(TEST_OFFLOAD_PACKET2).`when`(mockInterfaceAdvertiser1) doReturn(TEST_OFFLOAD_PACKET2).`when`(mockInterfaceAdvertiser1)
.getRawOffloadPayload( .getRawOffloadPayload(
SERVICE_ID_1 SERVICE_ID_1
@@ -265,6 +280,22 @@ class MdnsAdvertiserTest {
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
argThat { it.matches(ALL_NETWORKS_SERVICE) }) argThat { it.matches(ALL_NETWORKS_SERVICE) })
// Services are conflicted.
postSync { intAdvCbCaptor1.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
postSync { intAdvCbCaptor1.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
postSync { intAdvCbCaptor2.value.onServiceConflict(mockInterfaceAdvertiser2, SERVICE_ID_1) }
// Verify the metrics data
doReturn(10).`when`(mockInterfaceAdvertiser1).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(5).`when`(mockInterfaceAdvertiser2).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(22).`when`(mockInterfaceAdvertiser1).getSentPacketCount(SERVICE_ID_1)
doReturn(12).`when`(mockInterfaceAdvertiser2).getSentPacketCount(SERVICE_ID_1)
val metrics = postReturn { advertiser.getAdvertiserMetrics(SERVICE_ID_1) }
assertEquals(15, metrics.mRepliedRequestsCount)
assertEquals(34, metrics.mSentPacketCount)
assertEquals(2, metrics.mConflictDuringProbingCount)
assertEquals(1, metrics.mConflictAfterProbingCount)
// Unregister the service // Unregister the service
postSync { advertiser.removeService(SERVICE_ID_1) } postSync { advertiser.removeService(SERVICE_ID_1) }
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1) verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
@@ -376,6 +407,14 @@ class MdnsAdvertiserTest {
handler.post(r) handler.post(r)
handler.waitForIdle(TIMEOUT_MS) handler.waitForIdle(TIMEOUT_MS)
} }
private fun <T> postReturn(r: (() -> T)): T {
val future = CompletableFuture<T>()
handler.post {
future.complete(r())
}
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
}
} }
// NsdServiceInfo does not implement equals; this is useful to use in argument matchers // NsdServiceInfo does not implement equals; this is useful to use in argument matchers

View File

@@ -253,7 +253,7 @@ class MdnsAnnouncerTest {
val captor = ArgumentCaptor.forClass(DatagramPacket::class.java) val captor = ArgumentCaptor.forClass(DatagramPacket::class.java)
repeat(FIRST_ANNOUNCES_COUNT) { i -> repeat(FIRST_ANNOUNCES_COUNT) { i ->
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, request) verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, request, 1 /* sentPacketCount */)
verify(socket, atLeast(i + 1)).send(any()) verify(socket, atLeast(i + 1)).send(any())
val now = SystemClock.elapsedRealtime() val now = SystemClock.elapsedRealtime()
assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY) assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY)

View File

@@ -92,7 +92,7 @@ class MdnsProberTest {
private fun assertProbesSent(probeInfo: TestProbeInfo, expectedHex: String) { private fun assertProbesSent(probeInfo: TestProbeInfo, expectedHex: String) {
repeat(probeInfo.numSends) { i -> repeat(probeInfo.numSends) { i ->
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, probeInfo) verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, probeInfo, 1 /* sentPacketCount */)
// If the probe interval is short, more than (i+1) probes may have been sent already // If the probe interval is short, more than (i+1) probes may have been sent already
verify(socket, atLeast(i + 1)).send(any()) verify(socket, atLeast(i + 1)).send(any())
} }
@@ -190,7 +190,7 @@ class MdnsProberTest {
prober.startProbing(probeInfo) prober.startProbing(probeInfo)
// Expect the initial probe // Expect the initial probe
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(0, probeInfo) verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(0, probeInfo, 1 /* sentPacketCount */)
// Stop probing // Stop probing
val stopResult = CompletableFuture<Boolean>() val stopResult = CompletableFuture<Boolean>()
@@ -200,7 +200,7 @@ class MdnsProberTest {
// Wait for a bit (more than the probe delay) to ensure no more probes were sent // Wait for a bit (more than the probe delay) to ensure no more probes were sent
Thread.sleep(SHORT_TIMEOUT_MS * 2) Thread.sleep(SHORT_TIMEOUT_MS * 2)
verify(cb, never()).onSent(1, probeInfo) verify(cb, never()).onSent(1, probeInfo, 1 /* sentPacketCount */)
verify(cb, never()).onFinished(probeInfo) verify(cb, never()).onFinished(probeInfo)
// Only one sent packet // Only one sent packet

View File

@@ -155,7 +155,7 @@ class MdnsRecordRepositoryTest {
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1) val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
repository.onProbingSucceeded(probingInfo) repository.onProbingSucceeded(probingInfo)
repository.onAdvertisementSent(TEST_SERVICE_ID_1) repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1)) assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
repository.exitService(TEST_SERVICE_ID_1) repository.exitService(TEST_SERVICE_ID_1)
@@ -166,7 +166,7 @@ class MdnsRecordRepositoryTest {
fun testExitAnnouncements() { fun testExitAnnouncements() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME) val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1) repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1) repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1) val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
assertNotNull(exitAnnouncement) assertNotNull(exitAnnouncement)
@@ -195,7 +195,7 @@ class MdnsRecordRepositoryTest {
fun testExitAnnouncements_WithSubtype() { fun testExitAnnouncements_WithSubtype() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME) val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, TEST_SUBTYPE) repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, TEST_SUBTYPE)
repository.onAdvertisementSent(TEST_SERVICE_ID_1) repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1) val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
assertNotNull(exitAnnouncement) assertNotNull(exitAnnouncement)
@@ -230,7 +230,7 @@ class MdnsRecordRepositoryTest {
fun testExitingServiceReAdded() { fun testExitingServiceReAdded() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME) val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1) repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1) repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
repository.exitService(TEST_SERVICE_ID_1) repository.exitService(TEST_SERVICE_ID_1)
assertEquals(TEST_SERVICE_ID_1, assertEquals(TEST_SERVICE_ID_1,
@@ -246,7 +246,7 @@ class MdnsRecordRepositoryTest {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME) val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
TEST_SUBTYPE) TEST_SUBTYPE)
repository.onAdvertisementSent(TEST_SERVICE_ID_1) repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val packet = announcementInfo.getPacket(0) val packet = announcementInfo.getPacket(0)
assertEquals(0x8400 /* response, authoritative */, packet.flags) assertEquals(0x8400 /* response, authoritative */, packet.flags)
@@ -657,6 +657,34 @@ class MdnsRecordRepositoryTest {
// Above records are identical to the actual registrations: no conflict // Above records are identical to the actual registrations: no conflict
assertEquals(emptySet(), repository.getConflictingServices(packet)) assertEquals(emptySet(), repository.getConflictingServices(packet))
} }
@Test
fun testGetServiceRepliedRequestsCount() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
// Verify that there is no packet replied.
assertEquals(MdnsConstants.NO_PACKET,
repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_1))
val questions = listOf(MdnsPointerRecord(arrayOf("_testservice", "_tcp", "local"),
0L /* receiptTimeMillis */,
false /* cacheFlush */,
// TTL and data is empty for a question
0L /* ttlMillis */,
null /* pointer */))
val query = MdnsPacket(0 /* flags */, questions, listOf() /* answers */,
listOf() /* authorityRecords */, listOf() /* additionalRecords */)
val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
// Reply to the question and verify there is one packet replied.
val reply = repository.getReply(query, src)
assertNotNull(reply)
assertEquals(1, repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_1))
// No package replied for unknown service.
assertEquals(MdnsConstants.NO_PACKET,
repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_2))
}
} }
private fun MdnsRecordRepository.initWithService( private fun MdnsRecordRepository.initWithService(