diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 8728c3c647159..f4b745e3db4e4 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -697,6 +697,8 @@ public final class CredentialManagerService initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime()); MetricUtilities.logApiCalledInitialPhase(initMetric, session.mRequestSessionMetric.returnIncrementSequence()); + session.mRequestSessionMetric.getCandidateAggregateMetric().setSessionId( + initMetric.getSessionId()); } catch (Exception e) { Slog.i(TAG, "Unexpected error during metric logging: ", e); } diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index 40a4bc2468898..e1854fa3ed1dd 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -24,12 +24,14 @@ 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.CandidateAggregateMetric; 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.security.SecureRandom; import java.util.List; import java.util.Map; @@ -78,6 +80,15 @@ public class MetricUtilities { return sessUid; } + /** + * Used to help generate random sequences for local sessions, in the time-scale of credential + * manager flows. + * @return a high entropy int useful to use in reasonable time-frame sessions. + */ + public static int getHighlyUniqueInteger() { + return new SecureRandom().nextInt(); + } + /** * Given any two timestamps in nanoseconds, this gets the difference and converts to * milliseconds. Assumes the difference is not larger than the maximum int size. @@ -336,4 +347,63 @@ public class MetricUtilities { Slog.w(TAG, "Unexpected error during initial metric emit: " + e); } } + + /** + * A logging utility focused on track 1, where the calling app is known. This captures all + * aggregate information for the candidate phase. + * + * @param candidateAggregateMetric the aggregate candidate metric information collected + * @param sequenceNum the sequence number for this api call session emit + */ + public static void logApiCalledAggregateCandidate( + CandidateAggregateMetric candidateAggregateMetric, + int sequenceNum) { + try { + if (!LOG_FLAG) { + FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_TOTAL_REPORTED, + /*session_id*/ candidateAggregateMetric.getSessionId(), + /*sequence_num*/ sequenceNum, + /*query_returned*/ true, + /*num_query_providers*/ DEFAULT_INT_32, + /*min_query_start_timestamp_microseconds*/ + DEFAULT_INT_32, + /*max_query_end_timestamp_microseconds*/ + DEFAULT_INT_32, + /*query_response_unique_classtypes*/ + DEFAULT_REPEATED_STR, + /*query_per_classtype_counts*/ + DEFAULT_REPEATED_INT_32, + /*query_unique_entries*/ + DEFAULT_REPEATED_INT_32, + /*query_per_entry_counts*/ + DEFAULT_REPEATED_INT_32, + /*query_total_candidate_failure*/ + DEFAULT_INT_32, + /*query_framework_exception_unique_classtypes*/ + DEFAULT_REPEATED_STR, + /*query_per_exception_classtype_counts*/ + DEFAULT_REPEATED_INT_32, + /*auth_response_unique_classtypes*/ + DEFAULT_REPEATED_STR, + /*auth_per_classtype_counts*/ + DEFAULT_REPEATED_INT_32, + /*auth_unique_entries*/ + DEFAULT_REPEATED_INT_32, + /*auth_per_entry_counts*/ + DEFAULT_REPEATED_INT_32, + /*auth_total_candidate_failure*/ + DEFAULT_INT_32, + /*auth_framework_exception_unique_classtypes*/ + DEFAULT_REPEATED_STR, + /*auth_per_exception_classtype_counts*/ + DEFAULT_REPEATED_INT_32, + /*num_auth_clicks*/ + DEFAULT_INT_32, + /*auth_returned*/ false + ); + } + } catch (Exception e) { + Slog.w(TAG, "Unexpected error during metric logging: " + e); + } + } } diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 7caa921eacda5..350a0bef80847 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -132,7 +132,7 @@ abstract class RequestSession implements CredentialManagerUi.Credential mUserId, this, mEnabledProviders); mHybridService = context.getResources().getString( R.string.config_defaultCredentialManagerHybridService); - mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mRequestId, + mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType)); setCancellationListener(); } diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java new file mode 100644 index 0000000000000..25c2d9b6c112a --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java @@ -0,0 +1,50 @@ +/* + * 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; + +import com.android.server.credentials.ProviderSession; + +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. + */ +public class CandidateAggregateMetric { + + private static final String TAG = "CandidateProviderMetric"; + // The session id of this provider, default set to -1 + private int mSessionId = -1; + + public void setSessionId(int sessionId) { + mSessionId = sessionId; + } + + public int getSessionId() { + return mSessionId; + } + + /** + * This will take all the candidate data captured and aggregate that information. + * TODO(b/271135048) : Add on authentication entry outputs from track 2 here as well once + * generated + * @param providers a map that associates with all provider sessions + */ + public void collectAverages(Map providers) { + // TODO(b/271135048) : Complete this method + } +} diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateBrowsingPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateBrowsingPhaseMetric.java index 07af6549411ea..6b74252dec193 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidateBrowsingPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateBrowsingPhaseMetric.java @@ -27,23 +27,11 @@ package com.android.server.credentials.metrics; * though collection will begin in the candidate phase when the user begins browsing options. */ public class CandidateBrowsingPhaseMetric { - // The session id associated with the API Call this candidate provider is a part of, default -1 - private int mSessionId = -1; // The EntryEnum that was pressed, defaults to -1 private int mEntryEnum = EntryEnum.UNKNOWN.getMetricCode(); // The provider associated with the press, defaults to -1 private int mProviderUid = -1; - /* -- The session ID -- */ - - public void setSessionId(int sessionId) { - mSessionId = sessionId; - } - - public int getSessionId() { - return mSessionId; - } - /* -- The Entry of this tap -- */ public void setEntryEnum(int entryEnum) { 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 e77636ca3845e..b0016e764a395 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java @@ -119,7 +119,7 @@ public class ProviderSessionMetric { */ public void collectCandidateMetricSetupViaInitialMetric(InitialPhaseMetric initMetric) { try { - mCandidatePhasePerProviderMetric.setSessionId(initMetric.getSessionId()); + mCandidatePhasePerProviderMetric.setSessionId(MetricUtilities.getHighlyUniqueInteger()); mCandidatePhasePerProviderMetric.setServiceBeganTimeNanoseconds( initMetric.getCredentialServiceStartedTimeNanoseconds()); mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(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 4874378a8c218..0c43f6fab6927 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -22,11 +22,12 @@ import static com.android.server.credentials.MetricUtilities.generateMetricKey; import static com.android.server.credentials.MetricUtilities.logApiCalledCandidatePhase; import static com.android.server.credentials.MetricUtilities.logApiCalledFinalPhase; +import android.annotation.NonNull; import android.credentials.GetCredentialRequest; import android.credentials.ui.UserSelectionDialogResult; -import android.os.IBinder; import android.util.Slog; +import com.android.server.credentials.MetricUtilities; import com.android.server.credentials.ProviderSession; import java.util.ArrayList; @@ -53,6 +54,10 @@ public class RequestSessionMetric { mChosenProviderFinalPhaseMetric = new ChosenProviderFinalPhaseMetric(); // TODO(b/271135048) - Replace this with a new atom per each browsing emit (V4) protected List mCandidateBrowsingPhaseMetric = new ArrayList<>(); + // Specific aggregate candidate provider metric for the provider this session handles + @NonNull + protected final CandidateAggregateMetric mCandidateAggregateMetric = + new CandidateAggregateMetric(); public RequestSessionMetric() { } @@ -75,19 +80,25 @@ public class RequestSessionMetric { return mInitialPhaseMetric; } + /** + * @return the aggregate candidate phase metrics associated with the request session + */ + public CandidateAggregateMetric getCandidateAggregateMetric() { + return mCandidateAggregateMetric; + } + /** * Upon starting the service, this fills the initial phase metric properly. * * @param timestampStarted the timestamp the service begins at - * @param mRequestId the IBinder used to retrieve a unique id * @param mCallingUid the calling process's uid * @param metricCode typically pulled from {@link ApiName} */ - public void collectInitialPhaseMetricInfo(long timestampStarted, IBinder mRequestId, + public void collectInitialPhaseMetricInfo(long timestampStarted, int mCallingUid, int metricCode) { try { mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted); - mInitialPhaseMetric.setSessionId(mRequestId.hashCode()); + mInitialPhaseMetric.setSessionId(MetricUtilities.getHighlyUniqueInteger()); mInitialPhaseMetric.setCallerUid(mCallingUid); mInitialPhaseMetric.setApiName(metricCode); } catch (Exception e) { @@ -206,7 +217,6 @@ public class RequestSessionMetric { CandidatePhaseMetric selectedProviderPhaseMetric) { try { CandidateBrowsingPhaseMetric browsingPhaseMetric = new CandidateBrowsingPhaseMetric(); - browsingPhaseMetric.setSessionId(mInitialPhaseMetric.getSessionId()); browsingPhaseMetric.setEntryEnum( EntryEnum.getMetricCodeFromString(selection.getEntryKey())); browsingPhaseMetric.setProviderUid(selectedProviderPhaseMetric.getCandidateUid()); @@ -331,6 +341,20 @@ public class RequestSessionMetric { } } + /** + * Handles aggregate candidate phase metric emits in the RequestSession context, after the + * candidate phase completes. + * + * @param providers a map with known providers and their held metric objects + */ + public void logCandidateAggregateMetrics(Map providers) { + try { + mCandidateAggregateMetric.collectAverages(providers); + } catch (Exception e) { + Slog.i(TAG, "Unexpected error during aggregate candidate logging " + e); + } + } + /** * Handles the final logging for RequestSession context for the final phase. *