diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index e3d4c224f1fda..fe8a8c8979f7c 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -30,8 +30,6 @@ import android.os.RemoteException; import android.service.credentials.CallingAppInfo; import android.util.Slog; -import com.android.server.credentials.metrics.ProviderSessionMetric; - import java.util.ArrayList; import java.util.Set; @@ -95,12 +93,8 @@ public final class ClearRequestSession extends RequestSession primaryProviders, List providers, int userId, ISetEnabledProvidersCallback callback) { + final int callingUid = Binder.getCallingUid(); if (!hasWriteSecureSettingsPermission()) { try { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.FAILURE, callingUid); callback.onError( PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR); } catch (RemoteException e) { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking response: ", e); } return; @@ -744,10 +751,16 @@ public final class CredentialManagerService if (!writeEnabledStatus || !writePrimaryStatus) { Slog.e(TAG, "Failed to store setting containing enabled or primary providers"); try { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.FAILURE, callingUid); callback.onError( "failed_setting_store", "Failed to store setting containing enabled or primary providers"); } catch (RemoteException e) { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking error response: ", e); return; } @@ -755,8 +768,14 @@ public final class CredentialManagerService // Call the callback. try { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.SUCCESS, callingUid); callback.onResponse(); } catch (RemoteException e) { + MetricUtilities.logApiCalledSimpleV2( + ApiName.SET_ENABLED_PROVIDERS, + ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking response: ", e); // TODO: Propagate failure } @@ -805,10 +824,15 @@ public final class CredentialManagerService public List getCredentialProviderServices( int userId, int providerFilter) { verifyGetProvidersPermission(); + final int callingUid = Binder.getCallingUid(); + MetricUtilities.logApiCalledSimpleV2( + ApiName.GET_CREDENTIAL_PROVIDER_SERVICES, + ApiStatus.SUCCESS, callingUid); + return CredentialProviderInfoFactory + .getCredentialProviderServices( + mContext, userId, providerFilter, getEnabledProvidersForUser(userId), + getPrimaryProvidersForUserId(mContext, userId)); - return CredentialProviderInfoFactory.getCredentialProviderServices( - mContext, userId, providerFilter, getEnabledProvidersForUser(userId), - getPrimaryProvidersForUserId(mContext, userId)); } @Override diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index 6b5b7f3398f80..f95ed6adc8f6d 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -34,7 +34,6 @@ import android.service.credentials.CallingAppInfo; import android.service.credentials.PermissionUtils; import android.util.Slog; -import com.android.server.credentials.metrics.ProviderSessionMetric; import com.android.server.credentials.metrics.ProviderStatusForMetrics; import java.util.ArrayList; @@ -134,13 +133,8 @@ public class GetRequestSession extends RequestSession Integer.MAX_VALUE) { throw new ArithmeticException("Input timestamps are too far apart and unsupported"); } + if (t2 < t1) { + Slog.i(TAG, "The timestamps aren't in expected order, falling back to default int"); + return DEFAULT_INT_32; + } return (int) ((t2 - t1) / 1000); } @@ -184,7 +188,7 @@ public class MetricUtilities { finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(), /* framework_exception_unique_classtype */ finalPhaseMetric.getFrameworkException(), - /* primary_indicated */ false + /* primary_indicated */ finalPhaseMetric.isPrimary() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during final provider uid emit: " + e); @@ -222,7 +226,7 @@ public class MetricUtilities { /* auth_provider_status */ authenticationMetric.getProviderStatus(), /* query_returned */ - authenticationMetric.isQueryReturned() + authenticationMetric.isAuthReturned() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during candidate get metric logging: " + e); @@ -303,6 +307,7 @@ public class MetricUtilities { int[] candidateAuthEntryCountList = new int[providerSize]; int[] candidateRemoteEntryCountList = new int[providerSize]; String[] frameworkExceptionList = new String[providerSize]; + boolean[] candidatePrimaryProviderList = new boolean[providerSize]; int index = 0; for (var session : providerSessions) { CandidatePhaseMetric metric = session.mProviderSessionMetric @@ -335,6 +340,7 @@ public class MetricUtilities { candidateRemoteEntryCountList[index] = metric.getResponseCollective() .getCountForEntry(EntryEnum.REMOTE_ENTRY); frameworkExceptionList[index] = metric.getFrameworkException(); + candidatePrimaryProviderList[index] = metric.isPrimary(); index++; } FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED, @@ -368,7 +374,7 @@ public class MetricUtilities { /* api_name */ initialPhaseMetric.getApiName(), /* primary_candidates_indicated */ - DEFAULT_REPEATED_BOOL + candidatePrimaryProviderList ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during candidate provider uid metric emit: " + e); @@ -450,9 +456,13 @@ public class MetricUtilities { /*query_returned*/ candidateAggregateMetric.isQueryReturned(), /*num_query_providers*/ candidateAggregateMetric.getNumProviders(), /*min_query_start_timestamp_microseconds*/ - DEFAULT_INT_32, + getMetricTimestampDifferenceMicroseconds( + candidateAggregateMetric.getMinProviderTimestampNanoseconds(), + candidateAggregateMetric.getServiceBeganTimeNanoseconds()), /*max_query_end_timestamp_microseconds*/ - DEFAULT_INT_32, + getMetricTimestampDifferenceMicroseconds( + candidateAggregateMetric.getMaxProviderTimestampNanoseconds(), + candidateAggregateMetric.getServiceBeganTimeNanoseconds()), /*query_response_unique_classtypes*/ candidateAggregateMetric.getAggregateCollectiveQuery() .getUniqueResponseStrings(), @@ -466,11 +476,11 @@ public class MetricUtilities { candidateAggregateMetric.getAggregateCollectiveQuery() .getUniqueEntryCounts(), /*query_total_candidate_failure*/ - DEFAULT_INT_32, + candidateAggregateMetric.getTotalQueryFailures(), /*query_framework_exception_unique_classtypes*/ - DEFAULT_REPEATED_STR, + candidateAggregateMetric.getUniqueExceptionStringsQuery(), /*query_per_exception_classtype_counts*/ - DEFAULT_REPEATED_INT_32, + candidateAggregateMetric.getUniqueExceptionCountsQuery(), /*auth_response_unique_classtypes*/ candidateAggregateMetric.getAggregateCollectiveAuth() .getUniqueResponseStrings(), @@ -484,14 +494,15 @@ public class MetricUtilities { candidateAggregateMetric.getAggregateCollectiveAuth() .getUniqueEntryCounts(), /*auth_total_candidate_failure*/ - DEFAULT_INT_32, + candidateAggregateMetric.getTotalAuthFailures(), /*auth_framework_exception_unique_classtypes*/ - DEFAULT_REPEATED_STR, + candidateAggregateMetric.getUniqueExceptionStringsAuth(), /*auth_per_exception_classtype_counts*/ - DEFAULT_REPEATED_INT_32, + candidateAggregateMetric.getUniqueExceptionCountsAuth(), /*num_auth_clicks*/ candidateAggregateMetric.getNumAuthEntriesTapped(), - /*auth_returned*/ false + /*auth_returned*/ + candidateAggregateMetric.isAuthReturned() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during metric logging: " + e); @@ -556,7 +567,7 @@ public class MetricUtilities { /* clicked_entries */ browsedClickedEntries, /* provider_of_clicked_entry */ browsedProviderUid, /* api_status */ apiStatus, - /* primary_indicated */ false + /* primary_indicated */ finalPhaseMetric.isPrimary() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during metric logging: " + e); diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 068ca79281171..83f21afa1a324 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -214,9 +214,11 @@ public abstract class ProviderSession protected void updateStatusAndInvokeCallback(@NonNull Status status, CredentialsSource source) { setStatus(status); + boolean isPrimary = mProviderInfo != null && mProviderInfo.isPrimary(); mProviderSessionMetric.collectCandidateMetricUpdate(isTerminatingStatus(status), isCompletionStatus(status), mProviderSessionUid, - source == CredentialsSource.AUTH_ENTRY); + /*isAuthEntry*/source == CredentialsSource.AUTH_ENTRY, + /*isPrimary*/isPrimary); mCallbacks.onProviderStatusChanged(status, mComponentName, source); } /** Common method that transfers metrics from the init phase to candidates */ diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index e48534c9c0413..1988bb6e6e46d 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -304,6 +304,7 @@ abstract class RequestSession implements CredentialManagerUi.Credential * @param response the response associated with the API call that just completed */ protected void respondToClientWithResponseAndFinish(V response) { + mRequestSessionMetric.logCandidateAggregateMetrics(mProviders); mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(/*has_exception=*/ false, ProviderStatusForMetrics.FINAL_SUCCESS); if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) { @@ -337,6 +338,7 @@ abstract class RequestSession implements CredentialManagerUi.Credential * @param errorMsg the error message given back in the flow */ protected void respondToClientWithErrorAndFinish(String errorType, String errorMsg) { + mRequestSessionMetric.logCandidateAggregateMetrics(mProviders); mRequestSessionMetric.collectFinalPhaseProviderMetricStatus( /*has_exception=*/ true, ProviderStatusForMetrics.FINAL_FAILURE); if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) { @@ -359,4 +361,16 @@ abstract class RequestSession implements CredentialManagerUi.Credential mRequestSessionMetric.logFailureOrUserCancel(isUserCanceled); finishSession(/*propagateCancellation=*/false); } + + /** + * Reveals if a certain provider is primary after ensuring it exists at all in the designated + * provider info. + * + * @param componentName used to identify the provider we want to check primary status for + */ + protected boolean isPrimaryProviderViaProviderInfo(ComponentName componentName) { + var chosenProviderSession = mProviders.get(componentName.flattenToString()); + return chosenProviderSession != null && chosenProviderSession.mProviderInfo != null + && chosenProviderSession.mProviderInfo.isPrimary(); + } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java index fd497965b5b1c..cb6a5d06e35f5 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java @@ -24,8 +24,11 @@ import static android.credentials.ui.RequestInfo.TYPE_UNDEFINED; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_CLEAR_CREDENTIAL; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_CREATE_CREDENTIAL; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_GET_CREDENTIAL; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_GET_CREDENTIAL_PROVIDER_SERVICES; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_GET_CREDENTIAL_VIA_REGISTRY; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION; +import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_SET_ENABLED_PROVIDERS; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNKNOWN; import android.credentials.ui.RequestInfo; @@ -45,6 +48,20 @@ CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_GET_CREDENTIAL_VIA CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_CLEAR_CREDENTIAL), IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE( CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE + ), + + SET_ENABLED_PROVIDERS( + CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_SET_ENABLED_PROVIDERS), + + GET_CREDENTIAL_PROVIDER_SERVICES( + CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_GET_CREDENTIAL_PROVIDER_SERVICES), + + REGISTER_CREDENTIAL_DESCRIPTION( + CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION + ), + + UNREGISTER_CREDENTIAL_DESCRIPTION( + CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION ); private static final String TAG = "ApiName"; diff --git a/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java b/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java index 98ea5b977a925..62b8f24eb2013 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java @@ -25,10 +25,9 @@ import java.util.Map; * Contains information about what was collected from the authentication entry output. */ public class BrowsedAuthenticationMetric { - private static final String TAG = "BrowsedAuthenticationMetric"; + private static final String TAG = "AuthenticationMetric"; // 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; @@ -42,7 +41,7 @@ public class BrowsedAuthenticationMetric { // The status of this particular provider private int mProviderStatus = -1; // Indicates if this provider returned from the authentication entry query, default false - private boolean mQueryReturned = false; + private boolean mAuthReturned = false; // TODO(b/271135048) - Match the atom and provide a clean per provider session metric // encapsulation. @@ -84,12 +83,12 @@ public class BrowsedAuthenticationMetric { mProviderStatus = providerStatus; } - public void setQueryReturned(boolean queryReturned) { - mQueryReturned = queryReturned; + public void setAuthReturned(boolean authReturned) { + mAuthReturned = authReturned; } - public boolean isQueryReturned() { - return mQueryReturned; + public boolean isAuthReturned() { + return mAuthReturned; } public int getProviderStatus() { diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java index 05d048d0e79ec..339c221b0cccb 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java @@ -24,7 +24,8 @@ import java.util.Map; /** * This will generate most of its data via using the information of {@link CandidatePhaseMetric} - * across all the providers. This belongs to the metric flow where the calling app is known. + * across all the providers. This belongs to the metric flow where the calling app is known. It + * also contains {@link BrowsedAuthenticationMetric} data aggregated within. */ public class CandidateAggregateMetric { @@ -34,6 +35,9 @@ public class CandidateAggregateMetric { // Indicates if this provider returned from the candidate query phase, // true if at least one provider returns validly, even if empty, default false private boolean mQueryReturned = false; + // For reference, the initial log timestamp when the service started running the API call, + // defaults to -1 + private long mServiceBeganTimeNanoseconds = -1; // Indicates the total number of providers this aggregate captures information for, default 0 private int mNumProviders = 0; // Indicates if the authentication entry returned, true if at least one entry returns validly, @@ -47,6 +51,18 @@ public class CandidateAggregateMetric { // The combined aggregate collective across the auth entry info private ResponseCollective mAggregateCollectiveAuth = new ResponseCollective(Map.of(), Map.of()); + // The minimum of all the providers query start time, defaults to -1 + private long mMinProviderTimestampNanoseconds = -1; + // The maximum of all the providers query finish time, defaults to -1 + private long mMaxProviderTimestampNanoseconds = -1; + // The total number of failures across all the providers, defaults to 0 + private int mTotalQueryFailures = 0; + // The map of all seen framework exceptions and their counts across all providers, default empty + private Map mExceptionCountQuery = new LinkedHashMap<>(); + // The total number of failures across all auth entries, defaults to 0 + private int mTotalAuthFailures = 0; + // The map of all seen framework exceptions and their counts across auth entries, default empty + private Map mExceptionCountAuth = new LinkedHashMap<>(); public CandidateAggregateMetric(int sessionIdTrackOne) { mSessionIdProvider = sessionIdTrackOne; @@ -72,16 +88,33 @@ public class CandidateAggregateMetric { Map responseCountQuery = new LinkedHashMap<>(); Map entryCountQuery = new LinkedHashMap<>(); var providerSessions = providers.values(); + long min_query_start = Integer.MAX_VALUE; + long max_query_end = Integer.MIN_VALUE; for (var session : providerSessions) { var sessionMetric = session.getProviderSessionMetric(); var candidateMetric = sessionMetric.getCandidatePhasePerProviderMetric(); + if (mServiceBeganTimeNanoseconds == -1) { + mServiceBeganTimeNanoseconds = candidateMetric.getServiceBeganTimeNanoseconds(); + } mQueryReturned = mQueryReturned || candidateMetric.isQueryReturned(); ResponseCollective candidateCollective = candidateMetric.getResponseCollective(); ResponseCollective.combineTypeCountMaps(responseCountQuery, candidateCollective.getResponseCountsMap()); ResponseCollective.combineTypeCountMaps(entryCountQuery, candidateCollective.getEntryCountsMap()); + min_query_start = Math.min(min_query_start, + candidateMetric.getStartQueryTimeNanoseconds()); + max_query_end = Math.max(max_query_end, candidateMetric + .getQueryFinishTimeNanoseconds()); + mTotalQueryFailures += (candidateMetric.isHasException() ? 1 : 0); + if (!candidateMetric.getFrameworkException().isEmpty()) { + mExceptionCountQuery.put(candidateMetric.getFrameworkException(), + mExceptionCountQuery.getOrDefault( + candidateMetric.getFrameworkException(), 0) + 1); + } } + mMinProviderTimestampNanoseconds = min_query_start; + mMaxProviderTimestampNanoseconds = max_query_end; mAggregateCollectiveQuery = new ResponseCollective(responseCountQuery, entryCountQuery); } @@ -95,12 +128,18 @@ public class CandidateAggregateMetric { var authMetrics = sessionMetric.getBrowsedAuthenticationMetric(); mNumAuthEntriesTapped += authMetrics.size(); for (var authMetric : authMetrics) { - mAuthReturned = mAuthReturned || authMetric.isQueryReturned(); + mAuthReturned = mAuthReturned || authMetric.isAuthReturned(); ResponseCollective authCollective = authMetric.getAuthEntryCollective(); ResponseCollective.combineTypeCountMaps(responseCountAuth, authCollective.getResponseCountsMap()); ResponseCollective.combineTypeCountMaps(entryCountAuth, authCollective.getEntryCountsMap()); + mTotalQueryFailures += (authMetric.isHasException() ? 1 : 0); + if (!authMetric.getFrameworkException().isEmpty()) { + mExceptionCountQuery.put(authMetric.getFrameworkException(), + mExceptionCountQuery.getOrDefault( + authMetric.getFrameworkException(), 0) + 1); + } } } mAggregateCollectiveAuth = new ResponseCollective(responseCountAuth, entryCountAuth); @@ -130,4 +169,67 @@ public class CandidateAggregateMetric { public boolean isAuthReturned() { return mAuthReturned; } + + public long getMaxProviderTimestampNanoseconds() { + return mMaxProviderTimestampNanoseconds; + } + + public long getMinProviderTimestampNanoseconds() { + return mMinProviderTimestampNanoseconds; + } + + public int getTotalQueryFailures() { + return mTotalQueryFailures; + } + + /** + * Returns the unique, deduped, exception classtypes for logging associated with this provider. + * + * @return a string array for deduped exception classtypes + */ + public String[] getUniqueExceptionStringsQuery() { + String[] result = new String[mExceptionCountQuery.keySet().size()]; + mExceptionCountQuery.keySet().toArray(result); + return result; + } + + /** + * Returns the unique, deduped, exception classtype counts for logging associated with this + * provider. + * + * @return a string array for deduped classtype exception counts + */ + public int[] getUniqueExceptionCountsQuery() { + return mExceptionCountQuery.values().stream().mapToInt(Integer::intValue).toArray(); + } + + /** + * Returns the unique, deduped, exception classtypes for logging associated with this provider + * for auth entries. + * + * @return a string array for deduped exception classtypes for auth entries + */ + public String[] getUniqueExceptionStringsAuth() { + String[] result = new String[mExceptionCountAuth.keySet().size()]; + mExceptionCountAuth.keySet().toArray(result); + return result; + } + + /** + * Returns the unique, deduped, exception classtype counts for logging associated with this + * provider for auth entries. + * + * @return a string array for deduped classtype exception counts for auth entries + */ + public int[] getUniqueExceptionCountsAuth() { + return mExceptionCountAuth.values().stream().mapToInt(Integer::intValue).toArray(); + } + + public long getServiceBeganTimeNanoseconds() { + return mServiceBeganTimeNanoseconds; + } + + public int getTotalAuthFailures() { + return mTotalAuthFailures; + } } 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 d9bf4a134adb9..56140f367263a 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java @@ -59,6 +59,8 @@ public class CandidatePhaseMetric { // 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()); + // Indicates if this candidate is a primary provider, false by default + private boolean mIsPrimary = false; public CandidatePhaseMetric(int sessionIdTrackTwo) { mSessionIdProvider = sessionIdTrackTwo; @@ -185,4 +187,12 @@ public class CandidatePhaseMetric { public String getFrameworkException() { return mFrameworkException; } + + public void setPrimary(boolean primary) { + mIsPrimary = primary; + } + + public boolean isPrimary() { + return mIsPrimary; + } } 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 e8af86012aaf9..468d3c80707c7 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java @@ -76,6 +76,8 @@ public class ChosenProviderFinalPhaseMetric { // 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()); + // Indicates if this chosen provider was the primary provider, false by default + private boolean mIsPrimary = false; public ChosenProviderFinalPhaseMetric(int sessionIdCaller, int sessionIdProvider) { @@ -292,4 +294,12 @@ public class ChosenProviderFinalPhaseMetric { public int getSessionIdCaller() { return mSessionIdCaller; } + + public void setPrimary(boolean primary) { + mIsPrimary = primary; + } + + public boolean isPrimary() { + return mIsPrimary; + } } 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 629b2dfe55878..44d845eaaf43e 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java @@ -100,8 +100,8 @@ public class ProviderSessionMetric { */ public void collectAuthenticationExceptionStatus(boolean hasException) { try { - var mostRecentAuthenticationMetric = mBrowsedAuthenticationMetric - .get(mBrowsedAuthenticationMetric.size() - 1); + BrowsedAuthenticationMetric mostRecentAuthenticationMetric = + getUsedAuthenticationMetric(); mostRecentAuthenticationMetric.setHasException(hasException); } catch (Exception e) { Slog.i(TAG, "Error while setting authentication metric exception " + e); @@ -122,40 +122,44 @@ public class ProviderSessionMetric { private void collectAuthEntryUpdate(boolean isFailureStatus, boolean isCompletionStatus, int providerSessionUid) { - // 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. - var mostRecentAuthenticationMetric = mBrowsedAuthenticationMetric - .get(mBrowsedAuthenticationMetric.size() - 1); + BrowsedAuthenticationMetric mostRecentAuthenticationMetric = + getUsedAuthenticationMetric(); mostRecentAuthenticationMetric.setProviderUid(providerSessionUid); - // TODO(immediately) - add timestamps (no longer needed!!) but also update below values! if (isFailureStatus) { - mostRecentAuthenticationMetric.setQueryReturned(false); + mostRecentAuthenticationMetric.setAuthReturned(false); mostRecentAuthenticationMetric.setProviderStatus( ProviderStatusForMetrics.QUERY_FAILURE .getMetricCode()); } else if (isCompletionStatus) { - mostRecentAuthenticationMetric.setQueryReturned(true); + mostRecentAuthenticationMetric.setAuthReturned(true); mostRecentAuthenticationMetric.setProviderStatus( ProviderStatusForMetrics.QUERY_SUCCESS .getMetricCode()); } } + private BrowsedAuthenticationMetric getUsedAuthenticationMetric() { + return mBrowsedAuthenticationMetric + .get(mBrowsedAuthenticationMetric.size() - 1); + } + /** * Used to collect metrics at the update stage when a candidate provider gives back an update. * * @param isFailureStatus indicates the candidate provider sent back a terminated response * @param isCompletionStatus indicates the candidate provider sent back a completion response * @param providerSessionUid the uid of the provider + * @param isPrimary indicates if this candidate provider was the primary provider */ public void collectCandidateMetricUpdate(boolean isFailureStatus, - boolean isCompletionStatus, int providerSessionUid, boolean isAuthEntry) { + boolean isCompletionStatus, int providerSessionUid, boolean isAuthEntry, + boolean isPrimary) { try { if (isAuthEntry) { collectAuthEntryUpdate(isFailureStatus, isCompletionStatus, providerSessionUid); return; } + mCandidatePhasePerProviderMetric.setPrimary(isPrimary); mCandidatePhasePerProviderMetric.setCandidateUid(providerSessionUid); mCandidatePhasePerProviderMetric .setQueryFinishTimeNanoseconds(System.nanoTime()); 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 009cfa852bf40..281f3cc705be3 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -20,6 +20,7 @@ import static com.android.server.credentials.MetricUtilities.DEFAULT_INT_32; 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.logApiCalledAggregateCandidate; import static com.android.server.credentials.MetricUtilities.logApiCalledAuthenticationMetric; import static com.android.server.credentials.MetricUtilities.logApiCalledCandidateGetMetric; import static com.android.server.credentials.MetricUtilities.logApiCalledCandidatePhase; @@ -27,6 +28,7 @@ import static com.android.server.credentials.MetricUtilities.logApiCalledFinalPh import static com.android.server.credentials.MetricUtilities.logApiCalledNoUidFinal; import android.annotation.NonNull; +import android.content.ComponentName; import android.credentials.GetCredentialRequest; import android.credentials.ui.UserSelectionDialogResult; import android.util.Slog; @@ -279,6 +281,26 @@ public class RequestSessionMetric { } } + /** + * Used to update metrics when a response is received in a RequestSession. + * + * @param componentName the component name associated with the provider the response is for + */ + public void updateMetricsOnResponseReceived(Map providers, + ComponentName componentName, boolean isPrimary) { + try { + var chosenProviderSession = providers.get(componentName.flattenToString()); + if (chosenProviderSession != null) { + ProviderSessionMetric providerSessionMetric = + chosenProviderSession.getProviderSessionMetric(); + collectChosenMetricViaCandidateTransfer(providerSessionMetric + .getCandidatePhasePerProviderMetric(), isPrimary); + } + } catch (Exception e) { + Slog.i(TAG, "Exception upon candidate to chosen metric transfer: " + e); + } + } + /** * Called by RequestSessions upon chosen metric determination. It's expected that most bits * are transferred here. However, certain new information, such as the selected provider's final @@ -288,10 +310,13 @@ public class RequestSessionMetric { * {@link com.android.internal.util.FrameworkStatsLog} metric generation. * * @param candidatePhaseMetric the componentName to associate with a provider + * @param isPrimary indicates that this chosen provider is the primary provider (or not) */ - public void collectChosenMetricViaCandidateTransfer(CandidatePhaseMetric candidatePhaseMetric) { + public void collectChosenMetricViaCandidateTransfer(CandidatePhaseMetric candidatePhaseMetric, + boolean isPrimary) { try { mChosenProviderFinalPhaseMetric.setChosenUid(candidatePhaseMetric.getCandidateUid()); + mChosenProviderFinalPhaseMetric.setPrimary(isPrimary); mChosenProviderFinalPhaseMetric.setQueryPhaseLatencyMicroseconds( candidatePhaseMetric.getQueryLatencyMicroseconds()); @@ -357,6 +382,7 @@ public class RequestSessionMetric { public void logCandidateAggregateMetrics(Map providers) { try { mCandidateAggregateMetric.collectAverages(providers); + logApiCalledAggregateCandidate(mCandidateAggregateMetric, ++mSequenceCounter); } catch (Exception e) { Slog.i(TAG, "Unexpected error during aggregate candidate logging " + e); }