Extends the CandidateAverageMetric with Auth Entry

This adds the auth entries response collective into the candidate
average metric, thereby creating a full aggregate for the response
information. It also sets up the Get emit logic, and is a step towards
final completion of the collection system.

The threaded timed get emit system was removed in favor of an immediate
function-based buffer/queue logging system that will be added once all
changes are in.

Bug: 271135048
Test: Build and Won't Submit without E2E Test

Change-Id: I17ac83de23dee1486a10d7ce02bfc334ceef5581
This commit is contained in:
Arpan Kaphle
2023-05-01 02:07:56 +00:00
parent 06341694a0
commit 39942cfa7e
6 changed files with 217 additions and 9 deletions

View File

@@ -24,6 +24,7 @@ import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.credentials.metrics.BrowsedAuthenticationMetric;
import com.android.server.credentials.metrics.CandidateAggregateMetric;
import com.android.server.credentials.metrics.CandidateBrowsingPhaseMetric;
import com.android.server.credentials.metrics.CandidatePhaseMetric;
@@ -45,6 +46,7 @@ public class MetricUtilities {
private static final String TAG = "MetricUtilities";
public static final String USER_CANCELED_SUBSTRING = "TYPE_USER_CANCELED";
public static final int MIN_EMIT_WAIT_TIME_MS = 10;
public static final int DEFAULT_INT_32 = -1;
public static final String DEFAULT_STRING = "";
@@ -117,7 +119,8 @@ public class MetricUtilities {
}
/**
* A logging utility used primarily for the final phase of the current metric setup.
* A logging utility used primarily for the final phase of the current metric setup, focused on
* track 2, where the provider uid is known.
*
* @param finalPhaseMetric the coalesced data of the chosen provider
* @param browsingPhaseMetrics the coalesced data of the browsing phase
@@ -188,6 +191,56 @@ public class MetricUtilities {
}
}
/**
* This emits the authentication entry metrics for track 2, where the provider uid is known.
*
* @param authenticationMetric the authentication metric collection to emit with
*/
public static void logApiCalledAuthenticationMetric(
BrowsedAuthenticationMetric authenticationMetric) {
// TODO(immediately) - Add in this emit
}
/**
* A logging utility used primarily for the candidate phase's get responses in the current
* metric setup. This helps avoid nested proto-files. This is primarily focused on track 2,
* where the provider uid is known. It ensures to run in a separate thread while emitting
* the multiple atoms to work with expected emit limits.
*
* @param providers a map with known providers and their held metric objects
* @param emitSequenceId an emitted sequence id for the current session, that matches the
* candidate emit value, as these metrics belong with the candidates
*/
public static void logApiCalledCandidateGetMetric(Map<String, ProviderSession> providers,
int emitSequenceId) {
try {
// TODO(immediately) - Modify to a Static Queue of Ordered Functions and emit from
// queue to adhere to 10 second limit (thread removed given android safe-calling).
var sessions = providers.values();
for (var session : sessions) {
try {
var metric = session.getProviderSessionMetric()
.getCandidatePhasePerProviderMetric();
FrameworkStatsLog.write(
FrameworkStatsLog.CREDENTIAL_MANAGER_GET_REPORTED,
/* session_id */ metric.getSessionIdProvider(),
/* sequence_num */ emitSequenceId,
/* candidate_provider_uid */ metric.getCandidateUid(),
/* response_unique_classtypes */
metric.getResponseCollective().getUniqueResponseStrings(),
/* per_classtype_counts */
metric.getResponseCollective().getUniqueResponseCounts()
);
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception during get metric logging" + e);
}
}
} catch (Exception e) {
Slog.w(TAG, "Unexpected error during candidate get metric logging: " + e);
}
}
/**
* A logging utility used primarily for the candidate phase of the current metric setup. This
* will primarily focus on track 2, where the session id is associated with known providers,
@@ -369,13 +422,17 @@ public class MetricUtilities {
/*max_query_end_timestamp_microseconds*/
DEFAULT_INT_32,
/*query_response_unique_classtypes*/
DEFAULT_REPEATED_STR,
candidateAggregateMetric.getAggregateCollectiveQuery()
.getUniqueResponseStrings(),
/*query_per_classtype_counts*/
DEFAULT_REPEATED_INT_32,
candidateAggregateMetric.getAggregateCollectiveQuery()
.getUniqueResponseCounts(),
/*query_unique_entries*/
DEFAULT_REPEATED_INT_32,
candidateAggregateMetric.getAggregateCollectiveQuery()
.getUniqueEntries(),
/*query_per_entry_counts*/
DEFAULT_REPEATED_INT_32,
candidateAggregateMetric.getAggregateCollectiveQuery()
.getUniqueEntryCounts(),
/*query_total_candidate_failure*/
DEFAULT_INT_32,
/*query_framework_exception_unique_classtypes*/

View File

@@ -16,13 +16,23 @@
package com.android.server.credentials.metrics;
import com.android.server.credentials.metrics.shared.ResponseCollective;
import java.util.Map;
/**
* Encapsulates an authentication entry click atom, as a part of track 2.
* Contains information about what was collected from the authentication entry output.
*/
public class BrowsedAuthenticationMetric {
private static final String TAG = "BrowsedAuthenticationMetric";
// The session id of this provider known flow related metric
private final int mSessionIdProvider;
// The provider associated with the press, defaults to -1
private int mProviderUid = -1;
private ResponseCollective mAuthEntryCollective = new ResponseCollective(Map.of(), Map.of());
// TODO(b/271135048) - Match the atom and provide a clean per provider session metric
// encapsulation.
@@ -33,4 +43,21 @@ public class BrowsedAuthenticationMetric {
public int getSessionIdProvider() {
return mSessionIdProvider;
}
public void setProviderUid(int providerUid) {
mProviderUid = providerUid;
}
public int getProviderUid() {
return mProviderUid;
}
public void setAuthEntryCollective(
ResponseCollective authEntryCollective) {
this.mAuthEntryCollective = authEntryCollective;
}
public ResponseCollective getAuthEntryCollective() {
return mAuthEntryCollective;
}
}

View File

@@ -17,7 +17,9 @@
package com.android.server.credentials.metrics;
import com.android.server.credentials.ProviderSession;
import com.android.server.credentials.metrics.shared.ResponseCollective;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -35,6 +37,12 @@ public class CandidateAggregateMetric {
private int mNumProviders = 0;
// Indicates the total number of authentication entries that were tapped in aggregate, default 0
private int mNumAuthEntriesTapped = 0;
// The combined aggregate collective across the candidate get/create
private ResponseCollective mAggregateCollectiveQuery =
new ResponseCollective(Map.of(), Map.of());
// The combined aggregate collective across the auth entry info
private ResponseCollective mAggregateCollectiveAuth =
new ResponseCollective(Map.of(), Map.of());
public CandidateAggregateMetric(int sessionIdTrackOne) {
mSessionIdProvider = sessionIdTrackOne;
@@ -52,13 +60,44 @@ public class CandidateAggregateMetric {
*/
public void collectAverages(Map<String, ProviderSession> providers) {
// TODO(b/271135048) : Complete this method
collectQueryAggregates(providers);
collectAuthAggregates(providers);
}
private void collectQueryAggregates(Map<String, ProviderSession> providers) {
mNumProviders = providers.size();
Map<String, Integer> responseCountQuery = new LinkedHashMap<>();
Map<EntryEnum, Integer> entryCountQuery = new LinkedHashMap<>();
var providerSessions = providers.values();
for (var session : providerSessions) {
var metric = session.getProviderSessionMetric();
mQueryReturned = mQueryReturned || metric
.mCandidatePhasePerProviderMetric.isQueryReturned();
var sessionMetric = session.getProviderSessionMetric();
var candidateMetric = sessionMetric.getCandidatePhasePerProviderMetric();
mQueryReturned = mQueryReturned || candidateMetric.isQueryReturned();
ResponseCollective candidateCollective = candidateMetric.getResponseCollective();
ResponseCollective.combineTypeCountMaps(responseCountQuery,
candidateCollective.getResponseCountsMap());
ResponseCollective.combineTypeCountMaps(entryCountQuery,
candidateCollective.getEntryCountsMap());
}
mAggregateCollectiveQuery = new ResponseCollective(responseCountQuery, entryCountQuery);
}
private void collectAuthAggregates(Map<String, ProviderSession> providers) {
mNumProviders = providers.size();
Map<String, Integer> responseCountAuth = new LinkedHashMap<>();
Map<EntryEnum, Integer> entryCountAuth = new LinkedHashMap<>();
var providerSessions = providers.values();
for (var session : providerSessions) {
var sessionMetric = session.getProviderSessionMetric();
var authMetric = sessionMetric.getBrowsedAuthenticationMetric();
mQueryReturned = mQueryReturned; // TODO add auth info
ResponseCollective authCollective = authMetric.getAuthEntryCollective();
ResponseCollective.combineTypeCountMaps(responseCountAuth,
authCollective.getResponseCountsMap());
ResponseCollective.combineTypeCountMaps(entryCountAuth,
authCollective.getEntryCountsMap());
}
mAggregateCollectiveAuth = new ResponseCollective(responseCountAuth, entryCountAuth);
}
public int getNumProviders() {
@@ -69,7 +108,12 @@ public class CandidateAggregateMetric {
return mQueryReturned;
}
public int getNumAuthEntriesTapped() {
return mNumAuthEntriesTapped;
}
public ResponseCollective getAggregateCollectiveQuery() {
return mAggregateCollectiveQuery;
}
}

View File

@@ -63,6 +63,12 @@ public class ProviderSessionMetric {
return mCandidatePhasePerProviderMetric;
}
/**
* Retrieves the authentication clicked metric information.
*/
public BrowsedAuthenticationMetric getBrowsedAuthenticationMetric() {
return mBrowsedAuthenticationMetric;
}
/**
* This collects for ProviderSessions, with respect to the candidate providers, whether
@@ -91,6 +97,19 @@ public class ProviderSessionMetric {
// TODO(b/271135048) - Mimic typical candidate update, but with authentication metric
// Collect the final timestamps (and start timestamp), status, exceptions and the provider
// uid. This occurs typically *after* the collection is complete.
mBrowsedAuthenticationMetric.setProviderUid(providerSessionUid);
// TODO(immediately) - add timestamps
if (isFailureStatus) {
mCandidatePhasePerProviderMetric.setQueryReturned(false);
mCandidatePhasePerProviderMetric.setProviderQueryStatus(
ProviderStatusForMetrics.QUERY_FAILURE
.getMetricCode());
} else if (isCompletionStatus) {
mCandidatePhasePerProviderMetric.setQueryReturned(true);
mCandidatePhasePerProviderMetric.setProviderQueryStatus(
ProviderStatusForMetrics.QUERY_SUCCESS
.getMetricCode());
}
}
/**
@@ -240,7 +259,7 @@ public class ProviderSessionMetric {
if (!isAuthEntry) {
mCandidatePhasePerProviderMetric.setResponseCollective(responseCollective);
} else {
// TODO(b/immediately) - Add the auth entry get logic
mBrowsedAuthenticationMetric.setAuthEntryCollective(responseCollective);
}
}
}

View File

@@ -19,6 +19,7 @@ package com.android.server.credentials.metrics;
import static com.android.server.credentials.MetricUtilities.DELTA_EXCEPTION_CUT;
import static com.android.server.credentials.MetricUtilities.DELTA_RESPONSES_CUT;
import static com.android.server.credentials.MetricUtilities.generateMetricKey;
import static com.android.server.credentials.MetricUtilities.logApiCalledCandidateGetMetric;
import static com.android.server.credentials.MetricUtilities.logApiCalledCandidatePhase;
import static com.android.server.credentials.MetricUtilities.logApiCalledFinalPhase;
import static com.android.server.credentials.MetricUtilities.logApiCalledNoUidFinal;
@@ -343,6 +344,7 @@ public class RequestSessionMetric {
public void logCandidatePhaseMetrics(Map<String, ProviderSession> providers) {
try {
logApiCalledCandidatePhase(providers, ++mSequenceCounter, mInitialPhaseMetric);
logApiCalledCandidateGetMetric(providers, mSequenceCounter);
} catch (Exception e) {
Slog.i(TAG, "Unexpected error during candidate metric emit: " + e);
}

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import com.android.server.credentials.metrics.EntryEnum;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -69,6 +70,24 @@ public class ResponseCollective {
return result;
}
/**
* Returns an unmodifiable map of the entry counts, safe under the immutability of the
* class the original map is held within.
* @return an unmodifiable map of the entry : counts
*/
public Map<EntryEnum, Integer> getEntryCountsMap() {
return Collections.unmodifiableMap(mEntryCounts);
}
/**
* Returns an unmodifiable map of the response counts, safe under the immutability of the
* class the original map is held within.
* @return an unmodifiable map of the response : counts
*/
public Map<String, Integer> getResponseCountsMap() {
return Collections.unmodifiableMap(mResponseCounts);
}
/**
* Returns the unique, deduped, response classtype counts for logging associated with this
* provider.
@@ -112,4 +131,44 @@ public class ResponseCollective {
public int getNumEntriesTotal() {
return mEntryCounts.values().stream().mapToInt(Integer::intValue).sum();
}
/**
* This combines the current collective with another collective, only if that other
* collective is indeed a differing one in memory.
* @param other the other response collective to combine with
* @return a combined {@link ResponseCollective} object
*/
public ResponseCollective combineCollectives(ResponseCollective other) {
if (this == other) {
return this;
}
Map<String, Integer> responseCounts = new LinkedHashMap<>(other.mResponseCounts);
for (String response : mResponseCounts.keySet()) {
responseCounts.merge(response, mResponseCounts.get(response), Integer::sum);
}
Map<EntryEnum, Integer> entryCounts = new LinkedHashMap<>(other.mEntryCounts);
for (EntryEnum entry : mEntryCounts.keySet()) {
entryCounts.merge(entry, mEntryCounts.get(entry), Integer::sum);
}
return new ResponseCollective(responseCounts, entryCounts);
}
/**
* Given two maps of type : counts, this combines the second into the first, to get an aggregate
* deduped type:count output.
* @param first the first map of some type to counts used as the base
* @param second the second map of some type to counts that mixies with the first
* @param <T> The type of the object we are mix-deduping - i.e. responses or entries.
* @return the first map updated with the second map's information for type:counts
*/
public static <T> Map<T, Integer> combineTypeCountMaps(Map<T, Integer> first,
Map<T, Integer> second) {
for (T response : second.keySet()) {
first.merge(response, first.getOrDefault(response, 0), Integer::sum);
}
return first;
}
}