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:
@@ -24,16 +24,22 @@ import android.stats.connectivity.NsdEventType;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.ConnectivityStatsLog;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Class to record the NetworkNsdReported into statsd. Each client should create this class to
|
||||
* report its data.
|
||||
*/
|
||||
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.
|
||||
private final boolean mIsLegacy;
|
||||
// The client id.
|
||||
private final int mClientId;
|
||||
private final Dependencies mDependencies;
|
||||
private final Random mRandom;
|
||||
|
||||
public NetworkNsdReportedMetrics(boolean isLegacy, int clientId) {
|
||||
this(isLegacy, clientId, new Dependencies());
|
||||
@@ -44,6 +50,7 @@ public class NetworkNsdReportedMetrics {
|
||||
mIsLegacy = isLegacy;
|
||||
mClientId = clientId;
|
||||
mDependencies = dependencies;
|
||||
mRandom = dependencies.makeRandomGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +74,18 @@ public class NetworkNsdReportedMetrics {
|
||||
event.getFoundCallbackCount(),
|
||||
event.getLostCallbackCount(),
|
||||
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();
|
||||
builder.setIsLegacy(mIsLegacy);
|
||||
builder.setClientId(mClientId);
|
||||
builder.setRandomNumber(mRandom.nextInt(RANDOM_NUMBER_UPPER_BOUND));
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -113,14 +132,23 @@ public class NetworkNsdReportedMetrics {
|
||||
*
|
||||
* @param transactionId The transaction id of service registration.
|
||||
* @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();
|
||||
builder.setTransactionId(transactionId);
|
||||
builder.setType(NsdEventType.NET_REGISTER);
|
||||
builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED);
|
||||
builder.setEventDurationMillisec(durationMs);
|
||||
// TODO: Report repliedRequestsCount
|
||||
builder.setRepliedRequestsCount(repliedRequestsCount);
|
||||
builder.setSentPacketCount(sentPacketCount);
|
||||
builder.setConflictDuringProbingCount(conflictDuringProbingCount);
|
||||
builder.setConflictAfterProbingCount(conflictAfterProbingCount);
|
||||
mDependencies.statsWrite(builder.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
|
||||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
|
||||
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.util.MdnsUtils.Clock;
|
||||
|
||||
@@ -992,14 +994,20 @@ public class NsdService extends INsdManager.Stub {
|
||||
// instead of looking at the flag value.
|
||||
final long stopTimeMs = mClock.elapsedRealtime();
|
||||
if (request instanceof AdvertiserClientRequest) {
|
||||
final AdvertiserMetrics metrics =
|
||||
mAdvertiser.getAdvertiserMetrics(transactionId);
|
||||
mAdvertiser.removeService(transactionId);
|
||||
clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId,
|
||||
request.calculateRequestDurationMs(stopTimeMs));
|
||||
request.calculateRequestDurationMs(stopTimeMs), metrics);
|
||||
} else {
|
||||
if (unregisterService(transactionId)) {
|
||||
clientInfo.onUnregisterServiceSucceeded(clientRequestId,
|
||||
transactionId,
|
||||
request.calculateRequestDurationMs(stopTimeMs));
|
||||
request.calculateRequestDurationMs(stopTimeMs),
|
||||
new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
|
||||
NO_PACKET /* sentPacketCount */,
|
||||
0 /* conflictDuringProbingCount */,
|
||||
0 /* conflictAfterProbingCount */));
|
||||
} else {
|
||||
clientInfo.onUnregisterServiceFailed(
|
||||
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||
@@ -2496,9 +2504,14 @@ public class NsdService extends INsdManager.Stub {
|
||||
}
|
||||
|
||||
if (request instanceof AdvertiserClientRequest) {
|
||||
final AdvertiserMetrics metrics =
|
||||
mAdvertiser.getAdvertiserMetrics(transactionId);
|
||||
mAdvertiser.removeService(transactionId);
|
||||
mMetrics.reportServiceUnregistration(transactionId,
|
||||
request.calculateRequestDurationMs(mClock.elapsedRealtime()));
|
||||
request.calculateRequestDurationMs(mClock.elapsedRealtime()),
|
||||
metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
|
||||
metrics.mConflictDuringProbingCount,
|
||||
metrics.mConflictAfterProbingCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2524,7 +2537,11 @@ public class NsdService extends INsdManager.Stub {
|
||||
case NsdManager.REGISTER_SERVICE:
|
||||
unregisterService(transactionId);
|
||||
mMetrics.reportServiceUnregistration(transactionId,
|
||||
request.calculateRequestDurationMs(mClock.elapsedRealtime()));
|
||||
request.calculateRequestDurationMs(mClock.elapsedRealtime()),
|
||||
NO_PACKET /* repliedRequestsCount */,
|
||||
NO_PACKET /* sentPacketCount */,
|
||||
0 /* conflictDuringProbingCount */,
|
||||
0 /* conflictAfterProbingCount */);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -2663,8 +2680,11 @@ public class NsdService extends INsdManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs) {
|
||||
mMetrics.reportServiceUnregistration(transactionId, durationMs);
|
||||
void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs,
|
||||
AdvertiserMetrics metrics) {
|
||||
mMetrics.reportServiceUnregistration(transactionId, durationMs,
|
||||
metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
|
||||
metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
|
||||
try {
|
||||
mCb.onUnregisterServiceSucceeded(listenerKey);
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
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 android.annotation.NonNull;
|
||||
@@ -180,6 +181,7 @@ public class MdnsAdvertiser {
|
||||
// (with the old, conflicting, actually not used name as argument... The new
|
||||
// implementation will send callbacks with the new name).
|
||||
registration.mNotifiedRegistrationSuccess = false;
|
||||
registration.mConflictAfterProbingCount++;
|
||||
|
||||
// The service was done probing, just reset it to probing state (RFC6762 9.)
|
||||
forAllAdvertisers(a -> {
|
||||
@@ -195,6 +197,7 @@ public class MdnsAdvertiser {
|
||||
registration.updateForConflict(
|
||||
registration.makeNewServiceInfoForConflict(1 /* renameCount */),
|
||||
1 /* renameCount */);
|
||||
registration.mConflictDuringProbingCount++;
|
||||
|
||||
// Keep renaming if the new name conflicts in local registrations
|
||||
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
|
||||
public void onSocketCreated(@NonNull SocketKey socketKey,
|
||||
@NonNull MdnsInterfaceSocket socket,
|
||||
@@ -444,6 +463,8 @@ public class MdnsAdvertiser {
|
||||
private NsdServiceInfo mServiceInfo;
|
||||
@Nullable
|
||||
private final String mSubtype;
|
||||
int mConflictDuringProbingCount;
|
||||
int mConflictAfterProbingCount;
|
||||
|
||||
private Registration(@NonNull NsdServiceInfo serviceInfo, @Nullable String subtype) {
|
||||
this.mOriginalName = serviceInfo.getServiceName();
|
||||
@@ -555,6 +576,24 @@ public class MdnsAdvertiser {
|
||||
@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,
|
||||
@NonNull AdvertiserCallback cb, @NonNull SharedLog 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,
|
||||
@NonNull BiPredicate<K, V> predicate) {
|
||||
for (int i = 0; i < map.size(); i++) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public final class MdnsConstants {
|
||||
public static final int FLAG_TRUNCATED = 0x0200;
|
||||
public static final int QCLASS_INTERNET = 0x0001;
|
||||
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_PREFIX = "_";
|
||||
private static final String MDNS_IPV4_HOST_ADDRESS = "224.0.0.251";
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.connectivity.mdns;
|
||||
|
||||
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.LinkAddress;
|
||||
@@ -93,8 +95,11 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
||||
/**
|
||||
* Callbacks from {@link MdnsProber}.
|
||||
*/
|
||||
private class ProbingCallback implements
|
||||
PacketRepeaterCallback<MdnsProber.ProbingInfo> {
|
||||
private class ProbingCallback implements PacketRepeaterCallback<MdnsProber.ProbingInfo> {
|
||||
@Override
|
||||
public void onSent(int index, @NonNull MdnsProber.ProbingInfo info, int sentPacketCount) {
|
||||
mRecordRepository.onProbingSent(info.getServiceId(), sentPacketCount);
|
||||
}
|
||||
@Override
|
||||
public void onFinished(MdnsProber.ProbingInfo info) {
|
||||
final MdnsAnnouncer.AnnouncementInfo announcementInfo;
|
||||
@@ -118,8 +123,8 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
||||
*/
|
||||
private class AnnouncingCallback implements PacketRepeaterCallback<BaseAnnouncementInfo> {
|
||||
@Override
|
||||
public void onSent(int index, @NonNull BaseAnnouncementInfo info) {
|
||||
mRecordRepository.onAdvertisementSent(info.getServiceId());
|
||||
public void onSent(int index, @NonNull BaseAnnouncementInfo info, int sentPacketCount) {
|
||||
mRecordRepository.onAdvertisementSent(info.getServiceId(), sentPacketCount);
|
||||
}
|
||||
|
||||
@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.
|
||||
*
|
||||
|
||||
@@ -59,7 +59,7 @@ public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
|
||||
/**
|
||||
* 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.
|
||||
@@ -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 when the socket has not joined the relevant group.
|
||||
int sentPacketCount = 0;
|
||||
for (InetSocketAddress destination : ALL_ADDRS) {
|
||||
try {
|
||||
mReplySender.sendNow(packet, destination);
|
||||
sentPacketCount += mReplySender.sendNow(packet, destination);
|
||||
} catch (IOException 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
|
||||
if (mCb != null) {
|
||||
mCb.onSent(index, request);
|
||||
mCb.onSent(index, request, sentPacketCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.connectivity.mdns;
|
||||
|
||||
import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -174,6 +176,16 @@ public class MdnsRecordRepository {
|
||||
*/
|
||||
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).
|
||||
*
|
||||
@@ -181,7 +193,7 @@ public class MdnsRecordRepository {
|
||||
* @param serviceInfo Service to advertise
|
||||
*/
|
||||
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
|
||||
@Nullable String subtype) {
|
||||
@Nullable String subtype, int repliedServiceCount, int sentPacketCount) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
this.subtype = subtype;
|
||||
|
||||
@@ -254,6 +266,8 @@ public class MdnsRecordRepository {
|
||||
true /* sharedName */, true /* probing */));
|
||||
|
||||
this.allRecords = Collections.unmodifiableList(allRecords);
|
||||
this.repliedServiceCount = repliedServiceCount;
|
||||
this.sentPacketCount = sentPacketCount;
|
||||
}
|
||||
|
||||
void setProbing(boolean probing) {
|
||||
@@ -316,7 +330,8 @@ public class MdnsRecordRepository {
|
||||
}
|
||||
|
||||
final ServiceRegistration registration = new ServiceRegistration(
|
||||
mDeviceHostname, serviceInfo, subtype);
|
||||
mDeviceHostname, serviceInfo, subtype, NO_PACKET /* repliedServiceCount */,
|
||||
NO_PACKET /* sentPacketCount */);
|
||||
mServices.put(serviceId, registration);
|
||||
|
||||
// Remove existing exiting service
|
||||
@@ -405,6 +420,24 @@ public class MdnsRecordRepository {
|
||||
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
|
||||
* @return IDs of the removed services
|
||||
@@ -472,9 +505,12 @@ public class MdnsRecordRepository {
|
||||
for (int i = 0; i < mServices.size(); i++) {
|
||||
final ServiceRegistration registration = mServices.valueAt(i);
|
||||
if (registration.exiting) continue;
|
||||
addReplyFromService(question, registration.allRecords, registration.ptrRecords,
|
||||
if (addReplyFromService(question, registration.allRecords, registration.ptrRecords,
|
||||
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.
|
||||
*/
|
||||
private void addReplyFromService(@NonNull MdnsRecord question,
|
||||
private boolean addReplyFromService(@NonNull MdnsRecord question,
|
||||
@NonNull List<RecordInfo<?>> serviceRecords,
|
||||
@Nullable List<RecordInfo<MdnsPointerRecord>> servicePtrRecords,
|
||||
@Nullable RecordInfo<MdnsServiceRecord> serviceSrvRecord,
|
||||
@@ -596,7 +632,7 @@ public class MdnsRecordRepository {
|
||||
}
|
||||
|
||||
// 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<>();
|
||||
// RFC6763 12.1: if including PTR record, include the SRV and TXT records it names
|
||||
@@ -626,6 +662,7 @@ public class MdnsRecordRepository {
|
||||
addNsecRecordsForUniqueNames(additionalAnswerRecords,
|
||||
answerInfo.listIterator(answersStartIndex),
|
||||
additionalAnswerInfo.listIterator());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -862,8 +899,8 @@ public class MdnsRecordRepository {
|
||||
final ServiceRegistration existing = mServices.get(serviceId);
|
||||
if (existing == null) return null;
|
||||
|
||||
final ServiceRegistration newService = new ServiceRegistration(
|
||||
mDeviceHostname, newInfo, existing.subtype);
|
||||
final ServiceRegistration newService = new ServiceRegistration(mDeviceHostname, newInfo,
|
||||
existing.subtype, existing.repliedServiceCount, existing.sentPacketCount);
|
||||
mServices.put(serviceId, newService);
|
||||
return makeProbingInfo(serviceId, newService.srvRecord.record);
|
||||
}
|
||||
@@ -871,7 +908,7 @@ public class MdnsRecordRepository {
|
||||
/**
|
||||
* 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);
|
||||
if (registration == null) return;
|
||||
|
||||
@@ -880,8 +917,19 @@ public class MdnsRecordRepository {
|
||||
record.lastSentTimeMs = 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:
|
||||
* 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
|
||||
|
||||
@@ -44,6 +44,9 @@ import java.util.Collections;
|
||||
public class MdnsReplySender {
|
||||
private static final boolean DBG = MdnsAdvertiser.DBG;
|
||||
private static final int MSG_SEND = 1;
|
||||
private static final int PACKET_NOT_SENT = 0;
|
||||
private static final int PACKET_SENT = 1;
|
||||
|
||||
@NonNull
|
||||
private final MdnsInterfaceSocket mSocket;
|
||||
@NonNull
|
||||
@@ -79,16 +82,17 @@ public class 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 {
|
||||
ensureRunningOnHandlerThread(mHandler);
|
||||
if (!((destination.getAddress() instanceof Inet6Address && mSocket.hasJoinedIpv6())
|
||||
|| (destination.getAddress() instanceof Inet4Address && mSocket.hasJoinedIpv4()))) {
|
||||
// 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);
|
||||
mSocket.send(new DatagramPacket(outBuffer, 0, outBuffer.length, destination));
|
||||
return PACKET_SENT;
|
||||
}
|
||||
|
||||
/** Get the packetCreationBuffer */
|
||||
|
||||
@@ -64,6 +64,18 @@ message NetworkNsdReported {
|
||||
|
||||
// Record sent query count before stopped discovery
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,12 +21,15 @@ import android.stats.connectivity.MdnsQueryResult
|
||||
import android.stats.connectivity.NsdEventType
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import java.util.Random
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
@@ -34,6 +37,12 @@ import org.mockito.Mockito.verify
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
|
||||
class NetworkNsdReportedMetricsTest {
|
||||
private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java)
|
||||
private val random = mock(Random::class.java)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
doReturn(random).`when`(deps).makeRandomGenerator()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReportServiceRegistrationSucceeded() {
|
||||
@@ -80,8 +89,13 @@ class NetworkNsdReportedMetricsTest {
|
||||
val clientId = 99
|
||||
val transactionId = 100
|
||||
val durationMs = 10L
|
||||
val repliedRequestsCount = 25
|
||||
val sentPacketCount = 50
|
||||
val conflictDuringProbingCount = 2
|
||||
val conflictAfterProbingCount = 1
|
||||
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)
|
||||
verify(deps).statsWrite(eventCaptor.capture())
|
||||
@@ -92,6 +106,10 @@ class NetworkNsdReportedMetricsTest {
|
||||
assertEquals(NsdEventType.NET_REGISTER, it.type)
|
||||
assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, it.queryResult)
|
||||
assertEquals(durationMs, it.eventDurationMillisec)
|
||||
assertEquals(repliedRequestsCount, it.repliedRequestsCount)
|
||||
assertEquals(sentPacketCount, it.sentPacketCount)
|
||||
assertEquals(conflictDuringProbingCount, it.conflictDuringProbingCount)
|
||||
assertEquals(conflictAfterProbingCount, it.conflictAfterProbingCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1222,6 +1222,8 @@ public class NsdServiceTest {
|
||||
verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue());
|
||||
verify(mAdvertiser, never()).removeService(anyInt());
|
||||
|
||||
doReturn(mock(MdnsAdvertiser.AdvertiserMetrics.class))
|
||||
.when(mAdvertiser).getAdvertiserMetrics(anyInt());
|
||||
client.unregisterService(regListenerWithFeature);
|
||||
waitForIdle();
|
||||
verify(mAdvertiser).removeService(serviceIdCaptor.getValue());
|
||||
@@ -1312,14 +1314,20 @@ public class NsdServiceTest {
|
||||
new NsdServiceInfo(regInfo.getServiceName(), null))));
|
||||
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(metrics).when(mAdvertiser).getAdvertiserMetrics(regId);
|
||||
client.unregisterService(regListener);
|
||||
waitForIdle();
|
||||
verify(mAdvertiser).removeService(idCaptor.getValue());
|
||||
verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
|
||||
argThat(info -> matches(info, regInfo)));
|
||||
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
|
||||
|
||||
@@ -33,7 +33,10 @@ import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.waitForIdle
|
||||
import java.net.NetworkInterface
|
||||
import java.util.Objects
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
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).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)
|
||||
.getRawOffloadPayload(
|
||||
SERVICE_ID_1
|
||||
@@ -265,6 +280,22 @@ class MdnsAdvertiserTest {
|
||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
|
||||
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
|
||||
postSync { advertiser.removeService(SERVICE_ID_1) }
|
||||
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
|
||||
@@ -376,6 +407,14 @@ class MdnsAdvertiserTest {
|
||||
handler.post(r)
|
||||
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
|
||||
|
||||
@@ -253,7 +253,7 @@ class MdnsAnnouncerTest {
|
||||
|
||||
val captor = ArgumentCaptor.forClass(DatagramPacket::class.java)
|
||||
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())
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY)
|
||||
|
||||
@@ -92,7 +92,7 @@ class MdnsProberTest {
|
||||
|
||||
private fun assertProbesSent(probeInfo: TestProbeInfo, expectedHex: String) {
|
||||
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
|
||||
verify(socket, atLeast(i + 1)).send(any())
|
||||
}
|
||||
@@ -190,7 +190,7 @@ class MdnsProberTest {
|
||||
prober.startProbing(probeInfo)
|
||||
|
||||
// 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
|
||||
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
|
||||
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)
|
||||
|
||||
// Only one sent packet
|
||||
|
||||
@@ -155,7 +155,7 @@ class MdnsRecordRepositoryTest {
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
repository.onProbingSucceeded(probingInfo)
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
|
||||
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
repository.exitService(TEST_SERVICE_ID_1)
|
||||
@@ -166,7 +166,7 @@ class MdnsRecordRepositoryTest {
|
||||
fun testExitAnnouncements() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
|
||||
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)
|
||||
assertNotNull(exitAnnouncement)
|
||||
@@ -195,7 +195,7 @@ class MdnsRecordRepositoryTest {
|
||||
fun testExitAnnouncements_WithSubtype() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
|
||||
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)
|
||||
assertNotNull(exitAnnouncement)
|
||||
@@ -230,7 +230,7 @@ class MdnsRecordRepositoryTest {
|
||||
fun testExitingServiceReAdded() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
|
||||
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)
|
||||
|
||||
assertEquals(TEST_SERVICE_ID_1,
|
||||
@@ -246,7 +246,7 @@ class MdnsRecordRepositoryTest {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
|
||||
val announcementInfo = 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 packet = announcementInfo.getPacket(0)
|
||||
|
||||
assertEquals(0x8400 /* response, authoritative */, packet.flags)
|
||||
@@ -657,6 +657,34 @@ class MdnsRecordRepositoryTest {
|
||||
// Above records are identical to the actual registrations: no conflict
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user