Candidate Aggregate Metrics Setup + sessId Focus
This sets up the design for how candidate aggregate metrics will be collected, and runs through the whole flow up to emits. Currently, many values are default values, and will be filled in as the design is extended out. It does handle the split session id logic, however, between the two tracks. Also, after deliberation, we now use the hashCode of a random UUID to get closer to ~ (1/2^(32)) collision rate - their hashcode is pretty random, utilizing an int conversion from a bitwise xor operation of the most and least significant bits from the already high entropy uuid. This will provide more security than the binder implementation, especially under time and device id buckets, against collisions. Bug: 271135048 Test: Build and Won't Submit without E2E Test Change-Id: I8e7455dd6eaf8adb15035f6a9262f34a0c20531f
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ abstract class RequestSession<T, U, V> 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();
|
||||
}
|
||||
|
||||
@@ -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<String, ProviderSession> providers) {
|
||||
// TODO(b/271135048) : Complete this method
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<CandidateBrowsingPhaseMetric> 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<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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user