diff options
16 files changed, 405 insertions, 79 deletions
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index e9efe7eb6247..e3d4c224f1fd 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -30,6 +30,8 @@ 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; @@ -93,9 +95,12 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta public void onFinalResponseReceived( ComponentName componentName, Void response) { - mRequestSessionMetric.collectChosenMetricViaCandidateTransfer( - mProviders.get(componentName.flattenToString()).mProviderSessionMetric - .getCandidatePhasePerProviderMetric()); + if (mProviders.get(componentName.flattenToString()) != null) { + ProviderSessionMetric providerSessionMetric = + mProviders.get(componentName.flattenToString()).mProviderSessionMetric; + mRequestSessionMetric.collectChosenMetricViaCandidateTransfer(providerSessionMetric + .getCandidatePhasePerProviderMetric()); + } respondToClientWithResponseAndFinish(null); } diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index d3ea0fee332d..2d6e74f0ccf1 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -35,6 +35,7 @@ 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; @@ -131,9 +132,12 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR @Nullable CreateCredentialResponse response) { Slog.i(TAG, "Final credential received from: " + componentName.flattenToString()); mRequestSessionMetric.collectUiResponseData(/*uiReturned=*/ true, System.nanoTime()); - mRequestSessionMetric.collectChosenMetricViaCandidateTransfer(mProviders.get( - componentName.flattenToString()).mProviderSessionMetric - .getCandidatePhasePerProviderMetric()); + if (mProviders.get(componentName.flattenToString()) != null) { + ProviderSessionMetric providerSessionMetric = + mProviders.get(componentName.flattenToString()).mProviderSessionMetric; + mRequestSessionMetric.collectChosenMetricViaCandidateTransfer(providerSessionMetric + .getCandidatePhasePerProviderMetric()); + } if (response != null) { mRequestSessionMetric.collectChosenProviderStatus( ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode()); diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index aaf760fcfa67..9eb3b3b5b7cd 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -32,6 +32,7 @@ import android.os.RemoteException; import android.service.credentials.CallingAppInfo; import android.util.Slog; +import com.android.server.credentials.metrics.ProviderSessionMetric; import com.android.server.credentials.metrics.ProviderStatusForMetrics; import java.util.ArrayList; @@ -130,9 +131,12 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, @Nullable GetCredentialResponse response) { Slog.i(TAG, "onFinalResponseReceived from: " + componentName.flattenToString()); mRequestSessionMetric.collectUiResponseData(/*uiReturned=*/ true, System.nanoTime()); - mRequestSessionMetric.collectChosenMetricViaCandidateTransfer( - mProviders.get(componentName.flattenToString()) - .mProviderSessionMetric.getCandidatePhasePerProviderMetric()); + if (mProviders.get(componentName.flattenToString()) != null) { + ProviderSessionMetric providerSessionMetric = + mProviders.get(componentName.flattenToString()).mProviderSessionMetric; + mRequestSessionMetric.collectChosenMetricViaCandidateTransfer(providerSessionMetric + .getCandidatePhasePerProviderMetric()); + } if (response != null) { mRequestSessionMetric.collectChosenProviderStatus( ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode()); diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index 40a4bc246889..f9c44a94f89b 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; @@ -79,6 +81,15 @@ public class MetricUtilities { } /** + * 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. * @@ -130,7 +141,7 @@ public class MetricUtilities { index++; } FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINAL_PHASE_REPORTED, - /* session_id */ finalPhaseMetric.getSessionId(), + /* session_id */ finalPhaseMetric.getSessionIdProvider(), /* sequence_num */ emitSequenceId, /* ui_returned_final_start */ finalPhaseMetric.isUiReturned(), /* chosen_provider_uid */ finalPhaseMetric.getChosenUid(), @@ -170,8 +181,7 @@ public class MetricUtilities { finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(), /* framework_exception_unique_classtype */ finalPhaseMetric.getFrameworkException(), - /* primary_indicated */ - false + /* primary_indicated */ false ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during final provider uid emit: " + e); @@ -215,7 +225,7 @@ public class MetricUtilities { CandidatePhaseMetric metric = session.mProviderSessionMetric .getCandidatePhasePerProviderMetric(); if (sessionId == -1) { - sessionId = metric.getSessionId(); + sessionId = metric.getSessionIdProvider(); } if (!queryReturned) { queryReturned = metric.isQueryReturned(); @@ -319,7 +329,7 @@ public class MetricUtilities { FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_INIT_PHASE_REPORTED, /* api_name */ initialPhaseMetric.getApiName(), /* caller_uid */ initialPhaseMetric.getCallerUid(), - /* session_id */ initialPhaseMetric.getSessionId(), + /* session_id */ initialPhaseMetric.getSessionIdCaller(), /* sequence_num */ sequenceNum, /* initial_timestamp_reference_nanoseconds */ initialPhaseMetric.getCredentialServiceStartedTimeNanoseconds(), @@ -336,4 +346,129 @@ 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.getSessionIdProvider(), + /*sequence_num*/ sequenceNum, + /*query_returned*/ candidateAggregateMetric.isQueryReturned(), + /*num_providers*/ candidateAggregateMetric.getNumProviders(), + /*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); + } + } + + /** + * A logging utility used primarily for the final phase of the current metric setup for track 1. + * + * @param finalPhaseMetric the coalesced data of the chosen provider + * @param browsingPhaseMetrics the coalesced data of the browsing phase + * @param apiStatus the final status of this particular api call + * @param emitSequenceId an emitted sequence id for the current session + */ + public static void logApiCalledNoUidFinal(ChosenProviderFinalPhaseMetric finalPhaseMetric, + List<CandidateBrowsingPhaseMetric> browsingPhaseMetrics, int apiStatus, + int emitSequenceId) { + try { + if (!LOG_FLAG) { + return; + } + int browsedSize = browsingPhaseMetrics.size(); + int[] browsedClickedEntries = new int[browsedSize]; + int[] browsedProviderUid = new int[browsedSize]; + int index = 0; + for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) { + browsedClickedEntries[index] = metric.getEntryEnum(); + browsedProviderUid[index] = metric.getProviderUid(); + index++; + } + FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINALNOUID_REPORTED, + /* session_id */ finalPhaseMetric.getSessionIdCaller(), + /* sequence_num */ emitSequenceId, + /* ui_returned_final_start */ finalPhaseMetric.isUiReturned(), + /* chosen_provider_query_start_timestamp_microseconds */ + finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric + .getQueryStartTimeNanoseconds()), + /* chosen_provider_query_end_timestamp_microseconds */ + finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric + .getQueryEndTimeNanoseconds()), + /* chosen_provider_ui_invoked_timestamp_microseconds */ + finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric + .getUiCallStartTimeNanoseconds()), + /* chosen_provider_ui_finished_timestamp_microseconds */ + finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric + .getUiCallEndTimeNanoseconds()), + /* chosen_provider_finished_timestamp_microseconds */ + finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric + .getFinalFinishTimeNanoseconds()), + /* chosen_provider_status */ finalPhaseMetric.getChosenProviderStatus(), + /* chosen_provider_has_exception */ finalPhaseMetric.isHasException(), + /* 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_classtype */ + finalPhaseMetric.getFrameworkException(), + /* clicked_entries */ browsedClickedEntries, + /* provider_of_clicked_entry */ browsedProviderUid, + /* api_status */ apiStatus, + /* primary_indicated */ false + ); + } catch (Exception e) { + Slog.w(TAG, "Unexpected error during metric logging: " + e); + } + } + } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 409806a21679..25f20caee16d 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -193,11 +193,11 @@ public final class ProviderCreateSession extends ProviderSession< mProviderResponseDataHandler.addResponseContent(response.getCreateEntries(), response.getRemoteCreateEntry()); if (mProviderResponseDataHandler.isEmptyResponse(response)) { - mProviderSessionMetric.collectCandidateEntryMetrics(response); + mProviderSessionMetric.collectCandidateEntryMetrics(response, /*isAuthEntry*/false); updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } else { - mProviderSessionMetric.collectCandidateEntryMetrics(response); + mProviderSessionMetric.collectCandidateEntryMetrics(response, /*isAuthEntry*/false); updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 64438e338e5b..51af25b58992 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -433,6 +433,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential BeginGetCredentialResponse response = PendingIntentResultHandler .extractResponseContent(providerPendingIntentResponse .getResultData()); + mProviderSessionMetric.collectCandidateEntryMetrics(response, /*isAuthEntry*/true); if (response != null && !mProviderResponseDataHandler.isEmptyResponse(response)) { addToInitialRemoteResponse(response, /*isInitialResponse=*/ false); // Additional content received is in the form of new response content. @@ -470,12 +471,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential addToInitialRemoteResponse(response, /*isInitialResponse=*/true); // Log the data. if (mProviderResponseDataHandler.isEmptyResponse(response)) { - mProviderSessionMetric.collectCandidateEntryMetrics(response); + mProviderSessionMetric.collectCandidateEntryMetrics(response, /*isAuthEntry*/false); updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE, /*source=*/ CredentialsSource.REMOTE_PROVIDER); return; } - mProviderSessionMetric.collectCandidateEntryMetrics(response); + mProviderSessionMetric.collectCandidateEntryMetrics(response, /*isAuthEntry*/false); updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 04ddce4a6e60..068ca7928117 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -71,7 +71,7 @@ public abstract class ProviderSession<T, R> @NonNull protected Boolean mProviderResponseSet = false; @NonNull - protected final ProviderSessionMetric mProviderSessionMetric = new ProviderSessionMetric(); + protected final ProviderSessionMetric mProviderSessionMetric; @NonNull private int mProviderSessionUid; @@ -114,6 +114,13 @@ public abstract class ProviderSession<T, R> } /** + * Gives access to the objects metric collectors. + */ + public ProviderSessionMetric getProviderSessionMetric() { + return this.mProviderSessionMetric; + } + + /** * Interface to be implemented by any class that wishes to get a callback when a particular * provider session's status changes. Typically, implemented by the {@link RequestSession} * class. @@ -147,6 +154,8 @@ public abstract class ProviderSession<T, R> mComponentName = componentName; mRemoteCredentialService = remoteCredentialService; mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName); + mProviderSessionMetric = new ProviderSessionMetric( + ((RequestSession) mCallbacks).mRequestSessionMetric.getSessionIdTrackTwo()); } /** Provider status at various states of the provider session. */ @@ -206,7 +215,8 @@ public abstract class ProviderSession<T, R> CredentialsSource source) { setStatus(status); mProviderSessionMetric.collectCandidateMetricUpdate(isTerminatingStatus(status), - isCompletionStatus(status), mProviderSessionUid); + isCompletionStatus(status), mProviderSessionUid, + source == CredentialsSource.AUTH_ENTRY); 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 7caa921eacda..a41b5713ee14 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -75,6 +75,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential protected final Handler mHandler; @UserIdInt protected final int mUserId; + + protected final int mUniqueSessionInteger; private final int mCallingUid; @NonNull protected final CallingAppInfo mClientAppInfo; @@ -82,7 +84,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential protected final CancellationSignal mCancellationSignal; protected final Map<String, ProviderSession> mProviders = new ConcurrentHashMap<>(); - protected final RequestSessionMetric mRequestSessionMetric = new RequestSessionMetric(); + protected final RequestSessionMetric mRequestSessionMetric; protected final String mHybridService; protected final Object mLock; @@ -132,7 +134,10 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential mUserId, this, mEnabledProviders); mHybridService = context.getResources().getString( R.string.config_defaultCredentialManagerHybridService); - mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mRequestId, + mUniqueSessionInteger = MetricUtilities.getHighlyUniqueInteger(); + mRequestSessionMetric = new RequestSessionMetric(mUniqueSessionInteger, + MetricUtilities.getHighlyUniqueInteger()); + mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType)); setCancellationListener(); } diff --git a/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java b/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java new file mode 100644 index 000000000000..51e86d51acdf --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/BrowsedAuthenticationMetric.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * 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 { + // The session id of this provider known flow related metric + private final int mSessionIdProvider; + // TODO(b/271135048) - Match the atom and provide a clean per provider session metric + // encapsulation. + + public BrowsedAuthenticationMetric(int sessionIdProvider) { + mSessionIdProvider = sessionIdProvider; + } + + public int getSessionIdProvider() { + return mSessionIdProvider; + } +} 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 000000000000..08e75837a274 --- /dev/null +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateAggregateMetric.java @@ -0,0 +1,75 @@ +/* + * 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 metric + private final int mSessionIdProvider; + // Indicates if this provider returned from the query phase, default false + private boolean mQueryReturned = false; + // Indicates the total number of providers this aggregate captures information for, default 0 + private int mNumProviders = 0; + // Indicates the total number of authentication entries that were tapped in aggregate, default 0 + private int mNumAuthEntriesTapped = 0; + + public CandidateAggregateMetric(int sessionIdTrackOne) { + mSessionIdProvider = sessionIdTrackOne; + } + + public int getSessionIdProvider() { + return mSessionIdProvider; + } + + /** + * 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 the providers associated with the candidate flow + */ + public void collectAverages(Map<String, ProviderSession> providers) { + // TODO(b/271135048) : Complete this method + mNumProviders = providers.size(); + var providerSessions = providers.values(); + for (var session : providerSessions) { + var metric = session.getProviderSessionMetric(); + mQueryReturned = mQueryReturned || metric + .mCandidatePhasePerProviderMetric.isQueryReturned(); + } + } + + public int getNumProviders() { + return mNumProviders; + } + + public boolean isQueryReturned() { + return mQueryReturned; + } + + public int getNumAuthEntriesTapped() { + return mNumAuthEntriesTapped; + } +} 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 07af6549411e..6b74252dec19 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/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java index cb2e71f8ba9b..d9bf4a134adb 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java @@ -32,8 +32,8 @@ import java.util.Map; public class CandidatePhaseMetric { private static final String TAG = "CandidateProviderMetric"; - // The session id of this provider, default set to -1 - private int mSessionId = -1; + // The session id of this provider metric + private final int mSessionIdProvider; // Indicates if this provider returned from the query phase, default false private boolean mQueryReturned = false; @@ -60,7 +60,8 @@ public class CandidatePhaseMetric { // by default, contains empty info private ResponseCollective mResponseCollective = new ResponseCollective(Map.of(), Map.of()); - public CandidatePhaseMetric() { + public CandidatePhaseMetric(int sessionIdTrackTwo) { + mSessionIdProvider = sessionIdTrackTwo; } /* ---------- Latencies ---------- */ @@ -142,12 +143,8 @@ public class CandidatePhaseMetric { /* -------------- Session Id ---------------- */ - public void setSessionId(int sessionId) { - mSessionId = sessionId; - } - - public int getSessionId() { - return mSessionId; + public int getSessionIdProvider() { + return mSessionIdProvider; } /* -------------- Query Returned Status ---------------- */ 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 6af686587b88..e8af86012aaf 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java @@ -32,8 +32,12 @@ import java.util.Map; */ public class ChosenProviderFinalPhaseMetric { private static final String TAG = "ChosenFinalPhaseMetric"; - // The session id associated with this API call, used to unite split emits - private int mSessionId = -1; + // The session id associated with this API call, used to unite split emits, for the flow + // where we know the calling app + private final int mSessionIdCaller; + // The session id associated with this API call, used to unite split emits, for the flow + // where we know the provider apps + private final int mSessionIdProvider; // Reveals if the UI was returned, false by default private boolean mUiReturned = false; private int mChosenUid = -1; @@ -74,7 +78,9 @@ public class ChosenProviderFinalPhaseMetric { private ResponseCollective mResponseCollective = new ResponseCollective(Map.of(), Map.of()); - public ChosenProviderFinalPhaseMetric() { + public ChosenProviderFinalPhaseMetric(int sessionIdCaller, int sessionIdProvider) { + mSessionIdCaller = sessionIdCaller; + mSessionIdProvider = sessionIdProvider; } /* ------------------- UID ------------------- */ @@ -237,12 +243,8 @@ public class ChosenProviderFinalPhaseMetric { /* ----------- Session ID -------------- */ - public void setSessionId(int sessionId) { - mSessionId = sessionId; - } - - public int getSessionId() { - return mSessionId; + public int getSessionIdProvider() { + return mSessionIdProvider; } /* ----------- UI Returned Successfully -------------- */ @@ -284,4 +286,10 @@ public class ChosenProviderFinalPhaseMetric { public String getFrameworkException() { return mFrameworkException; } + + /* -------------- Session ID for Track One (Known Calling App) ---------------- */ + + public int getSessionIdCaller() { + return mSessionIdCaller; + } } 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 060e56ce965b..8e965e3e5ba5 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java @@ -32,8 +32,8 @@ public class InitialPhaseMetric { private int mApiName = ApiName.UNKNOWN.getMetricCode(); // The caller uid of the calling application, default to -1 private int mCallerUid = -1; - // The session id to unite multiple atom emits, default to -1 - private int mSessionId = -1; + // The session id to unite multiple atom emits + private final int mSessionIdCaller; // Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a // reference point. @@ -50,7 +50,8 @@ public class InitialPhaseMetric { private Map<String, Integer> mRequestCounts = new LinkedHashMap<>(); - public InitialPhaseMetric() { + public InitialPhaseMetric(int sessionIdTrackOne) { + mSessionIdCaller = sessionIdTrackOne; } /* ---------- Latencies ---------- */ @@ -105,12 +106,8 @@ public class InitialPhaseMetric { /* ------ SessionId ------ */ - public void setSessionId(int sessionId) { - mSessionId = sessionId; - } - - public int getSessionId() { - return mSessionId; + public int getSessionIdCaller() { + return mSessionIdCaller; } /* ------ Count Request Class Types ------ */ 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 e77636ca3845..47db8f59ff35 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java @@ -44,10 +44,17 @@ public class ProviderSessionMetric { // Specific candidate provider metric for the provider this session handles @NonNull - protected final CandidatePhaseMetric mCandidatePhasePerProviderMetric = - new CandidatePhaseMetric(); + protected final CandidatePhaseMetric mCandidatePhasePerProviderMetric; - public ProviderSessionMetric() {} + // IFF there was an authentication entry clicked, this stores all required information for + // that event. This is for the 'get' flow. + @NonNull + protected final BrowsedAuthenticationMetric mBrowsedAuthenticationMetric; + + public ProviderSessionMetric(int sessionIdTrackTwo) { + mCandidatePhasePerProviderMetric = new CandidatePhaseMetric(sessionIdTrackTwo); + mBrowsedAuthenticationMetric = new BrowsedAuthenticationMetric(sessionIdTrackTwo); + } /** * Retrieve the candidate provider phase metric and the data it contains. @@ -56,6 +63,7 @@ public class ProviderSessionMetric { return mCandidatePhasePerProviderMetric; } + /** * This collects for ProviderSessions, with respect to the candidate providers, whether * an exception occurred in the candidate call. @@ -78,6 +86,13 @@ 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. + } + /** * Used to collect metrics at the update stage when a candidate provider gives back an update. * @@ -86,8 +101,12 @@ public class ProviderSessionMetric { * @param providerSessionUid the uid of the provider */ public void collectCandidateMetricUpdate(boolean isFailureStatus, - boolean isCompletionStatus, int providerSessionUid) { + boolean isCompletionStatus, int providerSessionUid, boolean isAuthEntry) { try { + if (isAuthEntry) { + collectAuthEntryUpdate(isFailureStatus, isCompletionStatus, providerSessionUid); + return; + } mCandidatePhasePerProviderMetric.setCandidateUid(providerSessionUid); mCandidatePhasePerProviderMetric .setQueryFinishTimeNanoseconds(System.nanoTime()); @@ -119,7 +138,6 @@ public class ProviderSessionMetric { */ public void collectCandidateMetricSetupViaInitialMetric(InitialPhaseMetric initMetric) { try { - mCandidatePhasePerProviderMetric.setSessionId(initMetric.getSessionId()); mCandidatePhasePerProviderMetric.setServiceBeganTimeNanoseconds( initMetric.getCredentialServiceStartedTimeNanoseconds()); mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime()); @@ -133,13 +151,14 @@ public class ProviderSessionMetric { * purposes. * * @param response contains entries and data from the candidate provider responses + * @param isAuthEntry indicates if this is an auth entry collection or not * @param <R> the response type associated with the API flow in progress */ - public <R> void collectCandidateEntryMetrics(R response) { + public <R> void collectCandidateEntryMetrics(R response, boolean isAuthEntry) { try { if (response instanceof BeginGetCredentialResponse) { beginGetCredentialResponseCollectionCandidateEntryMetrics( - (BeginGetCredentialResponse) response); + (BeginGetCredentialResponse) response, isAuthEntry); } else if (response instanceof BeginCreateCredentialResponse) { beginCreateCredentialResponseCollectionCandidateEntryMetrics( (BeginCreateCredentialResponse) response); @@ -198,7 +217,7 @@ public class ProviderSessionMetric { } private void beginGetCredentialResponseCollectionCandidateEntryMetrics( - BeginGetCredentialResponse response) { + BeginGetCredentialResponse response, boolean isAuthEntry) { Map<EntryEnum, Integer> entryCounts = new LinkedHashMap<>(); Map<String, Integer> responseCounts = new LinkedHashMap<>(); int numCredEntries = response.getCredentialEntries().size(); @@ -217,6 +236,11 @@ public class ProviderSessionMetric { }); ResponseCollective responseCollective = new ResponseCollective(responseCounts, entryCounts); - mCandidatePhasePerProviderMetric.setResponseCollective(responseCollective); + + if (!isAuthEntry) { + mCandidatePhasePerProviderMetric.setResponseCollective(responseCollective); + } else { + // TODO(b/immediately) - Add the auth entry get logic + } } } 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 4874378a8c21..03ffe23f9886 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -21,10 +21,11 @@ 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.logApiCalledCandidatePhase; import static com.android.server.credentials.MetricUtilities.logApiCalledFinalPhase; +import static com.android.server.credentials.MetricUtilities.logApiCalledNoUidFinal; +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.ProviderSession; @@ -48,13 +49,24 @@ public class RequestSessionMetric { // As emits occur in sequential order, increment this counter and utilize protected int mSequenceCounter = 0; - protected final InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric(); + protected final InitialPhaseMetric mInitialPhaseMetric; protected final ChosenProviderFinalPhaseMetric - mChosenProviderFinalPhaseMetric = new ChosenProviderFinalPhaseMetric(); + mChosenProviderFinalPhaseMetric; // TODO(b/271135048) - Replace this with a new atom per each browsing emit (V4) protected List<CandidateBrowsingPhaseMetric> mCandidateBrowsingPhaseMetric = new ArrayList<>(); + // Specific aggregate candidate provider metric for the provider this session handles + @NonNull + protected final CandidateAggregateMetric mCandidateAggregateMetric; + // Since track two is shared, this allows provider sessions to capture a metric-specific + // session token for the flow where the provider is known + private final int mSessionIdTrackTwo; - public RequestSessionMetric() { + public RequestSessionMetric(int sessionIdTrackOne, int sessionIdTrackTwo) { + mSessionIdTrackTwo = sessionIdTrackTwo; + mInitialPhaseMetric = new InitialPhaseMetric(sessionIdTrackOne); + mCandidateAggregateMetric = new CandidateAggregateMetric(sessionIdTrackOne); + mChosenProviderFinalPhaseMetric = new ChosenProviderFinalPhaseMetric( + sessionIdTrackOne, sessionIdTrackTwo); } /** @@ -76,18 +88,25 @@ public class RequestSessionMetric { } /** + * @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} + * @param callingAppFlowUniqueInt the unique integer used as the session id for the calling app + * known flow */ - 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.setCallerUid(mCallingUid); mInitialPhaseMetric.setApiName(metricCode); } catch (Exception e) { @@ -206,7 +225,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()); @@ -274,7 +292,6 @@ public class RequestSessionMetric { */ public void collectChosenMetricViaCandidateTransfer(CandidatePhaseMetric candidatePhaseMetric) { try { - mChosenProviderFinalPhaseMetric.setSessionId(candidatePhaseMetric.getSessionId()); mChosenProviderFinalPhaseMetric.setChosenUid(candidatePhaseMetric.getCandidateUid()); mChosenProviderFinalPhaseMetric.setQueryPhaseLatencyMicroseconds( @@ -332,6 +349,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<String, ProviderSession> 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. * * @param apiStatus the final status of the api being called @@ -341,9 +372,15 @@ public class RequestSessionMetric { logApiCalledFinalPhase(mChosenProviderFinalPhaseMetric, mCandidateBrowsingPhaseMetric, apiStatus, ++mSequenceCounter); + logApiCalledNoUidFinal(mChosenProviderFinalPhaseMetric, mCandidateBrowsingPhaseMetric, + apiStatus, + ++mSequenceCounter); } catch (Exception e) { Slog.i(TAG, "Unexpected error during final metric emit: " + e); } } + public int getSessionIdTrackTwo() { + return mSessionIdTrackTwo; + } } |