From 79492ead514818ca8fac24d54b045015c28d4f69 Mon Sep 17 00:00:00 2001 From: Arpan Kaphle Date: Thu, 20 Apr 2023 23:50:44 +0000 Subject: [PATCH] Adding Final Phase {Deduped:Count} Objects This adds similar {Deduped:Count} logging to the final phase. However, due to the redundancy of data types, collections are generated to reduce some repitition. To ensure safety, the collections are made to be immutable. Bug: 271135048 Bug: 270568738 Bug: 278591970 Test: Build Complete, E2E In Progress (Won't Submit till Done) Change-Id: Id69a54532480bd5a998731b41926f5d72699c3c9 --- .../credentials/CredentialManagerService.java | 11 +- .../server/credentials/MetricUtilities.java | 61 +++++---- .../metrics/CandidatePhaseMetric.java | 110 ++------------- .../ChosenProviderFinalPhaseMetric.java | 114 +++------------- .../metrics/InitialPhaseMetric.java | 6 +- .../metrics/ProviderSessionMetric.java | 86 ++++++------ .../metrics/RequestSessionMetric.java | 17 +-- .../metrics/shared/ResponseCollective.java | 128 ++++++++++++++++++ 8 files changed, 241 insertions(+), 292 deletions(-) create mode 100644 services/credentials/java/com/android/server/credentials/metrics/shared/ResponseCollective.java diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 06b96eb46ac1d..f7932bfdc3f16 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -58,7 +58,6 @@ import android.provider.Settings; import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialProviderInfoFactory; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -472,7 +471,7 @@ public final class CredentialManagerService GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available on this device."); } catch (RemoteException e) { - Log.i( + Slog.i( TAG, "Issue invoking onError on IGetCredentialCallback " + "callback: " @@ -528,7 +527,7 @@ public final class CredentialManagerService false, null, false, false, null)); } catch (RemoteException e) { - Log.i( + Slog.i( TAG, "Issue invoking onError on IGetCredentialCallback " + "callback: " @@ -673,7 +672,7 @@ public final class CredentialManagerService MetricUtilities.logApiCalledInitialPhase(initMetric, session.mRequestSessionMetric.returnIncrementSequence()); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: ", e); + Slog.w(TAG, "Unexpected error during metric logging: ", e); } } @@ -706,7 +705,7 @@ public final class CredentialManagerService Settings.Secure.CREDENTIAL_SERVICE, storedValue, userId)) { - Log.e(TAG, "Failed to store setting containing enabled providers"); + Slog.e(TAG, "Failed to store setting containing enabled providers"); try { callback.onError( "failed_setting_store", @@ -962,7 +961,7 @@ public final class CredentialManagerService @Override @GuardedBy("mLock") public void onFinishRequestSession(@UserIdInt int userId, IBinder token) { - Log.i(TAG, "In onFinishRequestSession"); + Slog.i(TAG, "In onFinishRequestSession"); if (mRequestSessions.get(userId) != null) { mRequestSessions.get(userId).remove(token); } diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index 4e82ee71ac1b4..31c2a52f3c367 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -19,7 +19,7 @@ package com.android.server.credentials; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; -import android.util.Log; +import android.util.Slog; import com.android.internal.util.FrameworkStatsLog; import com.android.server.credentials.metrics.ApiName; @@ -27,6 +27,7 @@ import com.android.server.credentials.metrics.ApiStatus; import com.android.server.credentials.metrics.CandidateBrowsingPhaseMetric; import com.android.server.credentials.metrics.CandidatePhaseMetric; import com.android.server.credentials.metrics.ChosenProviderFinalPhaseMetric; +import com.android.server.credentials.metrics.EntryEnum; import com.android.server.credentials.metrics.InitialPhaseMetric; import java.util.List; @@ -69,7 +70,7 @@ public class MetricUtilities { componentName.getPackageName(), PackageManager.ApplicationInfoFlags.of(0)).uid; } catch (Throwable t) { - Log.i(TAG, "Couldn't find required uid"); + Slog.i(TAG, "Couldn't find required uid"); } return sessUid; } @@ -147,28 +148,28 @@ public class MetricUtilities { .getFinalFinishTimeNanoseconds()), /* chosen_provider_status */ finalPhaseMetric.getChosenProviderStatus(), /* chosen_provider_has_exception */ finalPhaseMetric.isHasException(), - /* chosen_provider_available_entries */ finalPhaseMetric.getAvailableEntries() - .stream().mapToInt(i -> i).toArray(), - /* chosen_provider_action_entry_count */ finalPhaseMetric.getActionEntryCount(), - /* chosen_provider_credential_entry_count */ - finalPhaseMetric.getCredentialEntryCount(), - /* chosen_provider_credential_entry_type_count */ - finalPhaseMetric.getCredentialEntryTypeCount(), - /* chosen_provider_remote_entry_count */ - finalPhaseMetric.getRemoteEntryCount(), - /* chosen_provider_authentication_entry_count */ - finalPhaseMetric.getAuthenticationEntryCount(), + /* chosen_provider_available_entries (deprecated) */ DEFAULT_REPEATED_INT_32, + /* chosen_provider_action_entry_count (deprecated) */ DEFAULT_INT_32, + /* chosen_provider_credential_entry_count (deprecated)*/DEFAULT_INT_32, + /* chosen_provider_credential_entry_type_count (deprecated) */ DEFAULT_INT_32, + /* chosen_provider_remote_entry_count (deprecated) */ DEFAULT_INT_32, + /* chosen_provider_authentication_entry_count (deprecated) */ DEFAULT_INT_32, /* clicked_entries */ browsedClickedEntries, /* provider_of_clicked_entry */ browsedProviderUid, /* api_status */ apiStatus, - DEFAULT_REPEATED_INT_32, - DEFAULT_REPEATED_INT_32, - DEFAULT_REPEATED_STR, - DEFAULT_REPEATED_INT_32, + /* unique_entries */ + finalPhaseMetric.getResponseCollective().getUniqueEntries(), + /* per_entry_counts */ + finalPhaseMetric.getResponseCollective().getUniqueEntryCounts(), + /* unique_response_classtypes */ + finalPhaseMetric.getResponseCollective().getUniqueResponseStrings(), + /* per_classtype_counts */ + finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(), + /* framework_exception_unique_classtypes */ DEFAULT_STRING ); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -223,12 +224,18 @@ public class MetricUtilities { metric.getQueryFinishTimeNanoseconds()); candidateStatusList[index] = metric.getProviderQueryStatus(); candidateHasExceptionList[index] = metric.isHasException(); - candidateTotalEntryCountList[index] = metric.getNumEntriesTotal(); - candidateCredentialEntryCountList[index] = metric.getCredentialEntryCount(); - candidateCredentialTypeCountList[index] = metric.getCredentialEntryTypeCount(); - candidateActionEntryCountList[index] = metric.getActionEntryCount(); - candidateAuthEntryCountList[index] = metric.getAuthenticationEntryCount(); - candidateRemoteEntryCountList[index] = metric.getRemoteEntryCount(); + candidateTotalEntryCountList[index] = metric.getResponseCollective() + .getNumEntriesTotal(); + candidateCredentialEntryCountList[index] = metric.getResponseCollective() + .getCountForEntry(EntryEnum.CREDENTIAL_ENTRY); + candidateCredentialTypeCountList[index] = metric.getResponseCollective() + .getUniqueResponseStrings().length; + candidateActionEntryCountList[index] = metric.getResponseCollective() + .getCountForEntry(EntryEnum.ACTION_ENTRY); + candidateAuthEntryCountList[index] = metric.getResponseCollective() + .getCountForEntry(EntryEnum.AUTHENTICATION_ENTRY); + candidateRemoteEntryCountList[index] = metric.getResponseCollective() + .getCountForEntry(EntryEnum.REMOTE_ENTRY); frameworkExceptionList[index] = metric.getFrameworkException(); index++; } @@ -262,7 +269,7 @@ public class MetricUtilities { initialPhaseMetric.getUniqueRequestCounts() ); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -298,7 +305,7 @@ public class MetricUtilities { DEFAULT_INT_32, /* chosen_provider_status */ DEFAULT_INT_32); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -331,7 +338,7 @@ public class MetricUtilities { initialPhaseMetric.isOriginSpecified() ); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java index b212606b8271a..e3e91cc54b29c 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java @@ -16,14 +16,12 @@ package com.android.server.credentials.metrics; -import android.util.IntArray; import android.util.Log; import com.android.server.credentials.MetricUtilities; +import com.android.server.credentials.metrics.shared.ResponseCollective; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; /** * The central candidate provider metric object that mimics our defined metric setup. @@ -55,23 +53,12 @@ public class CandidatePhaseMetric { private int mProviderQueryStatus = -1; // Indicates if an exception was thrown by this provider, false by default private boolean mHasException = false; - // Indicates the number of total entries available, defaults to -1 - private int mNumEntriesTotal = -1; - // The count of action entries from this provider, defaults to -1 - private int mActionEntryCount = -1; - // The count of credential entries from this provider, defaults to -1 - private int mCredentialEntryCount = -1; - // The *type-count* of the credential entries, defaults to -1 - private int mCredentialEntryTypeCount = -1; - // The count of remote entries from this provider, defaults to -1 - private int mRemoteEntryCount = -1; - // The count of authentication entries from this provider, defaults to -1 - private int mAuthenticationEntryCount = -1; - // Gathered to pass on to chosen provider when required - private final IntArray mAvailableEntries = new IntArray(); - // The *framework only* exception held by this provider, empty string by default private String mFrameworkException = ""; + // Stores the response credential information, as well as the response entry information which + // by default, contains empty info + private ResponseCollective mResponseCollective = new ResponseCollective(Map.of(), Map.of()); + public CandidatePhaseMetric() { } @@ -182,88 +169,13 @@ public class CandidatePhaseMetric { return mHasException; } - /* -------------- Number of Entries ---------------- */ - - public void setNumEntriesTotal(int numEntriesTotal) { - mNumEntriesTotal = numEntriesTotal; + /* -------------- The Entries and Responses Gathered ---------------- */ + public void setResponseCollective(ResponseCollective responseCollective) { + mResponseCollective = responseCollective; } - public int getNumEntriesTotal() { - return mNumEntriesTotal; - } - - /* -------------- Count of Action Entries ---------------- */ - - public void setActionEntryCount(int actionEntryCount) { - mActionEntryCount = actionEntryCount; - } - - public int getActionEntryCount() { - return mActionEntryCount; - } - - /* -------------- Count of Credential Entries ---------------- */ - - public void setCredentialEntryCount(int credentialEntryCount) { - mCredentialEntryCount = credentialEntryCount; - } - - public int getCredentialEntryCount() { - return mCredentialEntryCount; - } - - /* -------------- Count of Credential Entry Types ---------------- */ - - public void setCredentialEntryTypeCount(int credentialEntryTypeCount) { - mCredentialEntryTypeCount = credentialEntryTypeCount; - } - - public int getCredentialEntryTypeCount() { - return mCredentialEntryTypeCount; - } - - /* -------------- Count of Remote Entries ---------------- */ - - public void setRemoteEntryCount(int remoteEntryCount) { - mRemoteEntryCount = remoteEntryCount; - } - - public int getRemoteEntryCount() { - return mRemoteEntryCount; - } - - /* -------------- Count of Authentication Entries ---------------- */ - - public void setAuthenticationEntryCount(int authenticationEntryCount) { - mAuthenticationEntryCount = authenticationEntryCount; - } - - public int getAuthenticationEntryCount() { - return mAuthenticationEntryCount; - } - - /* -------------- The Entries Gathered ---------------- */ - - /** - * Allows adding an entry record to this metric collector, which can then be propagated to - * the final phase to retain information on the data available to the candidate. - * - * @param e the entry enum collected by the candidate provider associated with this metric - * collector - */ - public void addEntry(EntryEnum e) { - mAvailableEntries.add(e.getMetricCode()); - } - - /** - * Returns a safely copied list of the entries captured by this metric collector associated - * with a particular candidate provider. - * - * @return the full collection of entries encountered by the candidate provider associated with - * this metric - */ - public List getAvailableEntries() { - return Arrays.stream(mAvailableEntries.toArray()).boxed().collect(Collectors.toList()); + public ResponseCollective getResponseCollective() { + return mResponseCollective; } /* ------ Framework Exception for this Candidate ------ */ diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java index 8f08bb02dfd0c..64d33e6fdd766 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java @@ -19,9 +19,9 @@ package com.android.server.credentials.metrics; import android.util.Log; import com.android.server.credentials.MetricUtilities; +import com.android.server.credentials.metrics.shared.ResponseCollective; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; /** * The central chosen provider metric object that mimics our defined metric setup. This is used @@ -66,21 +66,10 @@ public class ChosenProviderFinalPhaseMetric { private int mChosenProviderStatus = -1; // Indicates if an exception was thrown by this provider, false by default private boolean mHasException = false; - // Indicates the number of total entries available, defaults to -1. Not presently emitted, but - // left as a utility - private int mNumEntriesTotal = -1; - // The count of action entries from this provider, defaults to -1 - private int mActionEntryCount = -1; - // The count of credential entries from this provider, defaults to -1 - private int mCredentialEntryCount = -1; - // The *type-count* of the credential entries, defaults to -1 - private int mCredentialEntryTypeCount = -1; - // The count of remote entries from this provider, defaults to -1 - private int mRemoteEntryCount = -1; - // The count of authentication entries from this provider, defaults to -1 - private int mAuthenticationEntryCount = -1; - // Gathered to pass on to chosen provider when required - private List mAvailableEntries = new ArrayList<>(); + + // Stores the response credential information, as well as the response entry information which + // by default, contains empty info + private ResponseCollective mResponseCollective = new ResponseCollective(Map.of(), Map.of()); public ChosenProviderFinalPhaseMetric() { @@ -264,87 +253,6 @@ public class ChosenProviderFinalPhaseMetric { return mUiReturned; } - /* -------------- Number of Entries ---------------- */ - - public void setNumEntriesTotal(int numEntriesTotal) { - mNumEntriesTotal = numEntriesTotal; - } - - public int getNumEntriesTotal() { - return mNumEntriesTotal; - } - - /* -------------- Count of Action Entries ---------------- */ - - public void setActionEntryCount(int actionEntryCount) { - mActionEntryCount = actionEntryCount; - } - - public int getActionEntryCount() { - return mActionEntryCount; - } - - /* -------------- Count of Credential Entries ---------------- */ - - public void setCredentialEntryCount(int credentialEntryCount) { - mCredentialEntryCount = credentialEntryCount; - } - - public int getCredentialEntryCount() { - return mCredentialEntryCount; - } - - /* -------------- Count of Credential Entry Types ---------------- */ - - public void setCredentialEntryTypeCount(int credentialEntryTypeCount) { - mCredentialEntryTypeCount = credentialEntryTypeCount; - } - - public int getCredentialEntryTypeCount() { - return mCredentialEntryTypeCount; - } - - /* -------------- Count of Remote Entries ---------------- */ - - public void setRemoteEntryCount(int remoteEntryCount) { - mRemoteEntryCount = remoteEntryCount; - } - - public int getRemoteEntryCount() { - return mRemoteEntryCount; - } - - /* -------------- Count of Authentication Entries ---------------- */ - - public void setAuthenticationEntryCount(int authenticationEntryCount) { - mAuthenticationEntryCount = authenticationEntryCount; - } - - public int getAuthenticationEntryCount() { - return mAuthenticationEntryCount; - } - - /* -------------- The Entries Gathered ---------------- */ - - /** - * Sets the collected list of entries from the candidate phase to be retrievable in the - * chosen phase in a semantically correct way. - */ - public void setAvailableEntries(List entries) { - mAvailableEntries = new ArrayList<>(entries); // no alias copy - } - - /** - * Returns a list of the entries captured by this metric collector associated - * with a particular chosen provider. - * - * @return the full collection of entries encountered by the chosen provider during the - * candidate phase. - */ - public List getAvailableEntries() { - return new ArrayList<>(mAvailableEntries); // no alias copy - } - /* -------------- Has Exception ---------------- */ public void setHasException(boolean hasException) { @@ -354,4 +262,14 @@ public class ChosenProviderFinalPhaseMetric { public boolean isHasException() { return mHasException; } + + /* -------------- The Entries and Responses Gathered ---------------- */ + + public void setResponseCollective(ResponseCollective responseCollective) { + mResponseCollective = responseCollective; + } + + public ResponseCollective getResponseCollective() { + return mResponseCollective; + } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java index 5cfb0e7d375b0..3f10109b52d54 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java @@ -48,7 +48,7 @@ public class InitialPhaseMetric { // Indicates if the origin was specified when making this API request private boolean mOriginSpecified = false; - // Stores the deduped request information, particularly {"req":5}. + // Stores the deduped request information, particularly {"req":5} private Map mRequestCounts = new LinkedHashMap<>(); @@ -138,7 +138,7 @@ public class InitialPhaseMetric { } /** - * Reruns the unique, deduped, request classtypes for logging. + * Returns the unique, deduped, request classtypes for logging. * @return a string array for deduped classtypes */ public String[] getUniqueRequestStrings() { @@ -151,7 +151,7 @@ public class InitialPhaseMetric { } /** - * Reruns the unique, deduped, request classtype counts for logging. + * Returns the unique, deduped, request classtype counts for logging. * @return a string array for deduped classtype counts */ public int[] getUniqueRequestCounts() { diff --git a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java index 9a88255ce973c..7b671469dc8c7 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java @@ -16,15 +16,19 @@ package com.android.server.credentials.metrics; +import static com.android.server.credentials.MetricUtilities.DELTA_CUT; +import static com.android.server.credentials.MetricUtilities.generateMetricKey; + import android.annotation.NonNull; import android.service.credentials.BeginCreateCredentialResponse; import android.service.credentials.BeginGetCredentialResponse; -import android.service.credentials.CredentialEntry; -import android.util.Log; +import android.util.Slog; import com.android.server.credentials.MetricUtilities; +import com.android.server.credentials.metrics.shared.ResponseCollective; -import java.util.stream.Collectors; +import java.util.LinkedHashMap; +import java.util.Map; /** * Provides contextual metric collection for objects generated from @@ -68,7 +72,7 @@ public class ProviderSessionMetric { try { mCandidatePhasePerProviderMetric.setFrameworkException(exceptionType); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -97,7 +101,7 @@ public class ProviderSessionMetric { .getMetricCode()); } } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -118,7 +122,7 @@ public class ProviderSessionMetric { initMetric.getCredentialServiceStartedTimeNanoseconds()); mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime()); } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } @@ -138,59 +142,53 @@ public class ProviderSessionMetric { beginCreateCredentialResponseCollectionCandidateEntryMetrics( (BeginCreateCredentialResponse) response); } else { - Log.i(TAG, "Your response type is unsupported for metric logging"); + Slog.i(TAG, "Your response type is unsupported for metric logging"); } } catch (Exception e) { - Log.w(TAG, "Unexpected error during metric logging: " + e); + Slog.w(TAG, "Unexpected error during metric logging: " + e); } } private void beginCreateCredentialResponseCollectionCandidateEntryMetrics( BeginCreateCredentialResponse response) { + Map entryCounts = new LinkedHashMap<>(); var createEntries = response.getCreateEntries(); - int numRemoteEntry = MetricUtilities.ZERO; - if (response.getRemoteCreateEntry() != null) { - numRemoteEntry = MetricUtilities.UNIT; - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.REMOTE_ENTRY); - } - int numCreateEntries = - createEntries == null ? MetricUtilities.ZERO : createEntries.size(); - if (numCreateEntries > MetricUtilities.ZERO) { - createEntries.forEach(c -> - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.CREDENTIAL_ENTRY)); - } - mCandidatePhasePerProviderMetric.setNumEntriesTotal(numCreateEntries + numRemoteEntry); - mCandidatePhasePerProviderMetric.setRemoteEntryCount(numRemoteEntry); - mCandidatePhasePerProviderMetric.setCredentialEntryCount(numCreateEntries); - mCandidatePhasePerProviderMetric.setCredentialEntryTypeCount(MetricUtilities.UNIT); + int numRemoteEntry = response.getRemoteCreateEntry() != null ? MetricUtilities.ZERO : + MetricUtilities.UNIT; + int numCreateEntries = createEntries.size(); + entryCounts.put(EntryEnum.REMOTE_ENTRY, numRemoteEntry); + entryCounts.put(EntryEnum.CREDENTIAL_ENTRY, numCreateEntries); + + Map responseCounts = new LinkedHashMap<>(); + responseCounts.put(MetricUtilities.DEFAULT_STRING, numCreateEntries); + // We don't store create response because it's directly related to the request + // We do still store the count, however + + ResponseCollective responseCollective = new ResponseCollective(responseCounts, entryCounts); + mCandidatePhasePerProviderMetric.setResponseCollective(responseCollective); } private void beginGetCredentialResponseCollectionCandidateEntryMetrics( BeginGetCredentialResponse response) { + Map entryCounts = new LinkedHashMap<>(); + Map responseCounts = new LinkedHashMap<>(); int numCredEntries = response.getCredentialEntries().size(); int numActionEntries = response.getActions().size(); int numAuthEntries = response.getAuthenticationActions().size(); - int numRemoteEntry = MetricUtilities.ZERO; - if (response.getRemoteCredentialEntry() != null) { - numRemoteEntry = MetricUtilities.UNIT; - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.REMOTE_ENTRY); - } - response.getCredentialEntries().forEach(c -> - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.CREDENTIAL_ENTRY)); - response.getActions().forEach(c -> - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.ACTION_ENTRY)); - response.getAuthenticationActions().forEach(c -> - mCandidatePhasePerProviderMetric.addEntry(EntryEnum.AUTHENTICATION_ENTRY)); - mCandidatePhasePerProviderMetric.setNumEntriesTotal(numCredEntries + numAuthEntries - + numActionEntries + numRemoteEntry); - mCandidatePhasePerProviderMetric.setCredentialEntryCount(numCredEntries); - int numTypes = (response.getCredentialEntries().stream() - .map(CredentialEntry::getType).collect( - Collectors.toSet())).size(); // Dedupe type strings - mCandidatePhasePerProviderMetric.setCredentialEntryTypeCount(numTypes); - mCandidatePhasePerProviderMetric.setActionEntryCount(numActionEntries); - mCandidatePhasePerProviderMetric.setAuthenticationEntryCount(numAuthEntries); - mCandidatePhasePerProviderMetric.setRemoteEntryCount(numRemoteEntry); + int numRemoteEntry = response.getRemoteCredentialEntry() != null ? MetricUtilities.ZERO : + MetricUtilities.UNIT; + entryCounts.put(EntryEnum.REMOTE_ENTRY, numRemoteEntry); + entryCounts.put(EntryEnum.CREDENTIAL_ENTRY, numCredEntries); + entryCounts.put(EntryEnum.ACTION_ENTRY, numActionEntries); + entryCounts.put(EntryEnum.AUTHENTICATION_ENTRY, numAuthEntries); + + response.getCredentialEntries().forEach(entry -> { + String entryKey = generateMetricKey(entry.getType(), DELTA_CUT); + responseCounts.put(entryKey, responseCounts.getOrDefault(entryKey, 0) + 1); + }); + + ResponseCollective responseCollective = new ResponseCollective(responseCounts, entryCounts); + mCandidatePhasePerProviderMetric.setResponseCollective(responseCollective); } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java index 4ecdfef401b6e..b999b301d5cff 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -272,21 +272,8 @@ public class RequestSessionMetric { candidatePhaseMetric.getStartQueryTimeNanoseconds()); mChosenProviderFinalPhaseMetric.setQueryEndTimeNanoseconds(candidatePhaseMetric .getQueryFinishTimeNanoseconds()); - - mChosenProviderFinalPhaseMetric.setNumEntriesTotal(candidatePhaseMetric - .getNumEntriesTotal()); - mChosenProviderFinalPhaseMetric.setCredentialEntryCount(candidatePhaseMetric - .getCredentialEntryCount()); - mChosenProviderFinalPhaseMetric.setCredentialEntryTypeCount( - candidatePhaseMetric.getCredentialEntryTypeCount()); - mChosenProviderFinalPhaseMetric.setActionEntryCount(candidatePhaseMetric - .getActionEntryCount()); - mChosenProviderFinalPhaseMetric.setRemoteEntryCount(candidatePhaseMetric - .getRemoteEntryCount()); - mChosenProviderFinalPhaseMetric.setAuthenticationEntryCount( - candidatePhaseMetric.getAuthenticationEntryCount()); - mChosenProviderFinalPhaseMetric.setAvailableEntries(candidatePhaseMetric - .getAvailableEntries()); + mChosenProviderFinalPhaseMetric.setResponseCollective( + candidatePhaseMetric.getResponseCollective()); mChosenProviderFinalPhaseMetric.setFinalFinishTimeNanoseconds(System.nanoTime()); } catch (Exception e) { Log.w(TAG, "Unexpected error during metric logging: " + e); diff --git a/services/credentials/java/com/android/server/credentials/metrics/shared/ResponseCollective.java b/services/credentials/java/com/android/server/credentials/metrics/shared/ResponseCollective.java new file mode 100644 index 0000000000000..951aca733b93e --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/shared/ResponseCollective.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.credentials.metrics.shared; + +import android.annotation.NonNull; +import android.util.Slog; + +import com.android.server.credentials.metrics.EntryEnum; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Some data is directly shared between the + * {@link com.android.server.credentials.metrics.CandidatePhaseMetric} and the + * {@link com.android.server.credentials.metrics.ChosenProviderFinalPhaseMetric}. This + * aims to create an abstraction that holds that information, to avoid duplication. + * + * This class should be immutable and threadsafe once generated. + */ +public class ResponseCollective { + /* + Abstract Function (responseCounts, entryCounts) -> A 'ResponseCollective' containing information + about a chosen or candidate providers available responses, be they entries or credentials. + + RepInvariant: mResponseCounts and mEntryCounts are always initialized + + Threadsafe and Immutability: Once generated, the maps remain unchangeable. The object is + threadsafe and immutable, and safe from external changes. This is threadsafe because it is + immutable after creation and only allows reads, not writes. + */ + + private static final String TAG = "ResponseCollective"; + + // Stores the deduped credential response information, eg {"response":5} for this provider + private final Map mResponseCounts; + // Stores the deduped entry information, eg {ENTRY_ENUM:5} for this provider + private final Map mEntryCounts; + + public ResponseCollective(@NonNull Map responseCounts, + @NonNull Map entryCounts) { + mResponseCounts = responseCounts == null ? new LinkedHashMap<>() : + new LinkedHashMap<>(responseCounts); + mEntryCounts = entryCounts == null ? new LinkedHashMap<>() : + new LinkedHashMap<>(entryCounts); + } + + /** + * Returns the unique, deduped, response classtypes for logging associated with this provider. + * + * @return a string array for deduped classtypes + */ + public String[] getUniqueResponseStrings() { + if (mResponseCounts.isEmpty()) { + Slog.w(TAG, "There are no unique string response types collected"); + } + String[] result = new String[mResponseCounts.keySet().size()]; + mResponseCounts.keySet().toArray(result); + return result; + } + + /** + * Returns the unique, deduped, response classtype counts for logging associated with this + * provider. + * @return a string array for deduped classtype counts + */ + public int[] getUniqueResponseCounts() { + if (mResponseCounts.isEmpty()) { + Slog.w(TAG, "There are no unique string response type counts collected"); + } + return mResponseCounts.values().stream().mapToInt(Integer::intValue).toArray(); + } + + /** + * Returns the unique, deduped, entry types for logging associated with this provider. + * @return an int array for deduped entries + */ + public int[] getUniqueEntries() { + if (mEntryCounts.isEmpty()) { + Slog.w(TAG, "There are no unique entry response types collected"); + } + return mEntryCounts.keySet().stream().mapToInt(Enum::ordinal).toArray(); + } + + /** + * Returns the unique, deduped, entry classtype counts for logging associated with this + * provider. + * @return a string array for deduped classtype counts + */ + public int[] getUniqueEntryCounts() { + if (mEntryCounts.isEmpty()) { + Slog.w(TAG, "There are no unique entry response type counts collected"); + } + return mEntryCounts.values().stream().mapToInt(Integer::intValue).toArray(); + } + + /** + * Given a specific {@link EntryEnum}, this provides us with the count of that entry within + * this particular provider. + * @param e the entry enum with which we want to know the count of + * @return a count of this particular entry enum stored by this provider + */ + public int getCountForEntry(EntryEnum e) { + return mEntryCounts.get(e); + } + + /** + * Indicates the total number of existing entries for this provider. + * @return a count of the total number of entries for this provider + */ + public int getNumEntriesTotal() { + return mEntryCounts.values().stream().mapToInt(Integer::intValue).sum(); + } +}