1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.credentials;
18 
19 import android.annotation.UserIdInt;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.credentials.CredentialManager;
24 import android.util.Slog;
25 
26 import com.android.internal.util.FrameworkStatsLog;
27 import com.android.server.credentials.metrics.ApiName;
28 import com.android.server.credentials.metrics.ApiStatus;
29 import com.android.server.credentials.metrics.BrowsedAuthenticationMetric;
30 import com.android.server.credentials.metrics.CandidateAggregateMetric;
31 import com.android.server.credentials.metrics.CandidateBrowsingPhaseMetric;
32 import com.android.server.credentials.metrics.CandidatePhaseMetric;
33 import com.android.server.credentials.metrics.ChosenProviderFinalPhaseMetric;
34 import com.android.server.credentials.metrics.EntryEnum;
35 import com.android.server.credentials.metrics.InitialPhaseMetric;
36 
37 import java.security.SecureRandom;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * For all future metric additions, this will contain their names for local usage after importing
43  * from {@link com.android.internal.util.FrameworkStatsLog}.
44  */
45 public class MetricUtilities {
46     private static final boolean LOG_FLAG = true;
47 
48     private static final String TAG = CredentialManager.TAG;
49     public static final String USER_CANCELED_SUBSTRING = "TYPE_USER_CANCELED";
50     public static final int MIN_EMIT_WAIT_TIME_MS = 10;
51 
52     public static final int DEFAULT_INT_32 = -1;
53     public static final String DEFAULT_STRING = "";
54     public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
55     public static final String[] DEFAULT_REPEATED_STR = new String[0];
56     public static final boolean[] DEFAULT_REPEATED_BOOL = new boolean[0];
57     // Used for single count metric emits, such as singular amounts of various types
58     public static final int UNIT = 1;
59     // Used for zero count metric emits, such as zero amounts of various types
60     public static final int ZERO = 0;
61     // The number of characters at the end of the string to use as a key
62     public static final int DELTA_RESPONSES_CUT = 20;
63     // The cut for exception strings from the end - used to keep metrics small
64     public static final int DELTA_EXCEPTION_CUT = 30;
65 
66     /**
67      * This retrieves the uid of any package name, given a context and a component name for the
68      * package. By default, if the desired package uid cannot be found, it will fall back to a
69      * bogus uid.
70      *
71      * @return the uid of a given package
72      */
getPackageUid(Context context, ComponentName componentName, @UserIdInt int userId)73     protected static int getPackageUid(Context context, ComponentName componentName,
74             @UserIdInt int userId) {
75         if (componentName == null) {
76             return -1;
77         }
78         return getPackageUid(context, componentName.getPackageName(), userId);
79     }
80 
81     /** Returns the package uid, or -1 if not found. */
getPackageUid(Context context, String packageName, @UserIdInt int userId)82     public static int getPackageUid(Context context, String packageName,
83             @UserIdInt int userId) {
84         if (packageName == null) {
85             return -1;
86         }
87         try {
88             return context.getPackageManager().getPackageUidAsUser(packageName,
89                     PackageManager.PackageInfoFlags.of(0), userId);
90         } catch (Throwable t) {
91             Slog.i(TAG, "Couldn't find uid for " + packageName + ": " + t);
92             return -1;
93         }
94     }
95 
96     /**
97      * Used to help generate random sequences for local sessions, in the time-scale of credential
98      * manager flows.
99      * @return a high entropy int useful to use in reasonable time-frame sessions.
100      */
getHighlyUniqueInteger()101     public static int getHighlyUniqueInteger() {
102         return new SecureRandom().nextInt();
103     }
104 
105     /**
106      * Given any two timestamps in nanoseconds, this gets the difference and converts to
107      * milliseconds. Assumes the difference is not larger than the maximum int size.
108      *
109      * @param t2 the final timestamp
110      * @param t1 the initial timestamp
111      * @return the timestamp difference converted to microseconds
112      */
getMetricTimestampDifferenceMicroseconds(long t2, long t1)113     protected static int getMetricTimestampDifferenceMicroseconds(long t2, long t1) {
114         if (t2 - t1 > Integer.MAX_VALUE) {
115             Slog.i(TAG, "Input timestamps are too far apart and unsupported, "
116                     + "falling back to default int");
117             return DEFAULT_INT_32;
118         }
119         if (t2 < t1) {
120             Slog.i(TAG, "The timestamps aren't in expected order, falling back to default int");
121             return DEFAULT_INT_32;
122         }
123         return (int) ((t2 - t1) / 1000);
124     }
125 
126     /**
127      * Given the current design, we can designate how the strings in the backend should appear.
128      * This helper method lets us cut strings for our class types.
129      *
130      * @param classtype the classtype string we want to cut to generate a key
131      * @param deltaFromEnd the starting point from the end of the string we wish to begin at
132      * @return the cut up string key we want to use for metric logs
133      */
generateMetricKey(String classtype, int deltaFromEnd)134     public static String generateMetricKey(String classtype, int deltaFromEnd) {
135         return classtype.substring(classtype.length() - deltaFromEnd);
136     }
137 
138     /**
139      * A logging utility used primarily for the final phase of the current metric setup, focused on
140      * track 2, where the provider uid is known.
141      *
142      * @param finalPhaseMetric     the coalesced data of the chosen provider
143      * @param browsingPhaseMetrics the coalesced data of the browsing phase
144      * @param apiStatus            the final status of this particular api call
145      * @param emitSequenceId       an emitted sequence id for the current session
146      */
logApiCalledFinalPhase(ChosenProviderFinalPhaseMetric finalPhaseMetric, List<CandidateBrowsingPhaseMetric> browsingPhaseMetrics, int apiStatus, int emitSequenceId)147     public static void logApiCalledFinalPhase(ChosenProviderFinalPhaseMetric finalPhaseMetric,
148             List<CandidateBrowsingPhaseMetric> browsingPhaseMetrics, int apiStatus,
149             int emitSequenceId) {
150         try {
151             if (!LOG_FLAG) {
152                 return;
153             }
154             int browsedSize = browsingPhaseMetrics.size();
155             int[] browsedClickedEntries = new int[browsedSize];
156             int[] browsedProviderUid = new int[browsedSize];
157             int index = 0;
158             for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) {
159                 browsedClickedEntries[index] = metric.getEntryEnum();
160                 browsedProviderUid[index] = metric.getProviderUid();
161                 index++;
162             }
163             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINAL_PHASE_REPORTED,
164                     /* session_id */ finalPhaseMetric.getSessionIdProvider(),
165                     /* sequence_num */ emitSequenceId,
166                     /* ui_returned_final_start */ finalPhaseMetric.isUiReturned(),
167                     /* chosen_provider_uid */ finalPhaseMetric.getChosenUid(),
168                     /* chosen_provider_query_start_timestamp_microseconds */
169                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
170                             .getQueryStartTimeNanoseconds()),
171                     /* chosen_provider_query_end_timestamp_microseconds */
172                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
173                             .getQueryEndTimeNanoseconds()),
174                     /* chosen_provider_ui_invoked_timestamp_microseconds */
175                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
176                             .getUiCallStartTimeNanoseconds()),
177                     /* chosen_provider_ui_finished_timestamp_microseconds */
178                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
179                             .getUiCallEndTimeNanoseconds()),
180                     /* chosen_provider_finished_timestamp_microseconds */
181                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
182                             .getFinalFinishTimeNanoseconds()),
183                     /* chosen_provider_status */ finalPhaseMetric.getChosenProviderStatus(),
184                     /* chosen_provider_has_exception */ finalPhaseMetric.isHasException(),
185                     /* chosen_provider_available_entries (deprecated) */ DEFAULT_REPEATED_INT_32,
186                     /* chosen_provider_action_entry_count (deprecated) */ DEFAULT_INT_32,
187                     /* chosen_provider_credential_entry_count (deprecated)*/DEFAULT_INT_32,
188                     /* chosen_provider_credential_entry_type_count (deprecated) */ DEFAULT_INT_32,
189                     /* chosen_provider_remote_entry_count (deprecated) */ DEFAULT_INT_32,
190                     /* chosen_provider_authentication_entry_count (deprecated) */ DEFAULT_INT_32,
191                     /* clicked_entries */ browsedClickedEntries,
192                     /* provider_of_clicked_entry */ browsedProviderUid,
193                     /* api_status */ apiStatus,
194                     /* unique_entries */
195                     finalPhaseMetric.getResponseCollective().getUniqueEntries(),
196                     /* per_entry_counts */
197                     finalPhaseMetric.getResponseCollective().getUniqueEntryCounts(),
198                     /* unique_response_classtypes */
199                     finalPhaseMetric.getResponseCollective().getUniqueResponseStrings(),
200                     /* per_classtype_counts */
201                     finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(),
202                     /* framework_exception_unique_classtype */
203                     finalPhaseMetric.getFrameworkException(),
204                     /* primary_indicated */ finalPhaseMetric.isPrimary()
205             );
206         } catch (Exception e) {
207             Slog.w(TAG, "Unexpected error during final provider uid emit: " + e);
208         }
209     }
210 
211     /**
212      * This emits the authentication entry metrics for track 2, where the provider uid is known.
213      *
214      * @param authenticationMetric the authentication metric collection to emit with
215      * @param emitSequenceId       an emitted sequence id for the current session
216      */
logApiCalledAuthenticationMetric( BrowsedAuthenticationMetric authenticationMetric, int emitSequenceId)217     public static void logApiCalledAuthenticationMetric(
218             BrowsedAuthenticationMetric authenticationMetric,
219             int emitSequenceId) {
220         try {
221             if (!LOG_FLAG) {
222                 return;
223             }
224             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED,
225                     /* session_id */ authenticationMetric.getSessionIdProvider(),
226                     /* sequence_num */ emitSequenceId,
227                     /* chosen_provider_uid */ authenticationMetric.getProviderUid(),
228                     /* unique_response_classtypes */
229                     authenticationMetric.getAuthEntryCollective().getUniqueResponseStrings(),
230                     /* per_classtype_counts */
231                     authenticationMetric.getAuthEntryCollective().getUniqueResponseCounts(),
232                     /* unique_entries */
233                     authenticationMetric.getAuthEntryCollective().getUniqueEntries(),
234                     /* auth_per_entry_counts */
235                     authenticationMetric.getAuthEntryCollective().getUniqueEntryCounts(),
236                     /* framework_exception_unique_classtype */
237                     authenticationMetric.getFrameworkException(),
238                     /* exception_specified */ authenticationMetric.isHasException(),
239                     /* auth_provider_status */
240                     authenticationMetric.getProviderStatus(),
241                     /* query_returned */
242                     authenticationMetric.isAuthReturned()
243             );
244         } catch (Exception e) {
245             Slog.w(TAG, "Unexpected error during candidate auth metric logging: " + e);
246         }
247     }
248 
249     /**
250      * A logging utility used primarily for the candidate phase's get responses in the current
251      * metric setup. This helps avoid nested proto-files. This is primarily focused on track 2,
252      * where the provider uid is known. It ensures to run in a separate thread while emitting
253      * the multiple atoms to work with expected emit limits.
254      *
255      * @param providers      a map with known providers and their held metric objects
256      * @param emitSequenceId an emitted sequence id for the current session, that matches the
257      *                       candidate emit value, as these metrics belong with the candidates
258      */
logApiCalledCandidateGetMetric(Map<String, ProviderSession> providers, int emitSequenceId)259     public static void logApiCalledCandidateGetMetric(Map<String, ProviderSession> providers,
260             int emitSequenceId) {
261         try {
262             // TODO(b/274954697) : To queue format in future optimizations (metrics POC support)
263             if (!LOG_FLAG) {
264                 return;
265             }
266             var sessions = providers.values();
267             for (var session : sessions) {
268                 var metric = session.getProviderSessionMetric()
269                         .getCandidatePhasePerProviderMetric();
270                 FrameworkStatsLog.write(
271                         FrameworkStatsLog.CREDENTIAL_MANAGER_GET_REPORTED,
272                         /* session_id */ metric.getSessionIdProvider(),
273                         /* sequence_num */ emitSequenceId,
274                         /* candidate_provider_uid */ metric.getCandidateUid(),
275                         /* response_unique_classtypes */
276                         metric.getResponseCollective().getUniqueResponseStrings(),
277                         /* per_classtype_counts */
278                         metric.getResponseCollective().getUniqueResponseCounts()
279                 );
280             }
281         } catch (Exception e) {
282             Slog.w(TAG, "Unexpected error during candidate get metric logging: " + e);
283         }
284     }
285 
286 
287     /**
288      * A logging utility used primarily for the candidate phase of the current metric setup. This
289      * will primarily focus on track 2, where the session id is associated with known providers,
290      * but NOT the calling app.
291      *
292      * @param providers      a map with known providers and their held metric objects
293      * @param emitSequenceId an emitted sequence id for the current session
294      * @param initialPhaseMetric contains initial phase data to avoid repetition for candidate
295      *                           phase, track 2, logging
296      */
logApiCalledCandidatePhase(Map<String, ProviderSession> providers, int emitSequenceId, InitialPhaseMetric initialPhaseMetric)297     public static void logApiCalledCandidatePhase(Map<String, ProviderSession> providers,
298             int emitSequenceId, InitialPhaseMetric initialPhaseMetric) {
299         try {
300             if (!LOG_FLAG) {
301                 return;
302             }
303             var providerSessions = providers.values();
304             int providerSize = providerSessions.size();
305             int sessionId = -1;
306             boolean queryReturned = false;
307             int[] candidateUidList = new int[providerSize];
308             int[] candidateQueryStartTimeStampList = new int[providerSize];
309             int[] candidateQueryEndTimeStampList = new int[providerSize];
310             int[] candidateStatusList = new int[providerSize];
311             boolean[] candidateHasExceptionList = new boolean[providerSize];
312             int[] candidateTotalEntryCountList = new int[providerSize];
313             int[] candidateCredentialEntryCountList = new int[providerSize];
314             int[] candidateCredentialTypeCountList = new int[providerSize];
315             int[] candidateActionEntryCountList = new int[providerSize];
316             int[] candidateAuthEntryCountList = new int[providerSize];
317             int[] candidateRemoteEntryCountList = new int[providerSize];
318             String[] frameworkExceptionList = new String[providerSize];
319             boolean[] candidatePrimaryProviderList = new boolean[providerSize];
320             int index = 0;
321             for (var session : providerSessions) {
322                 CandidatePhaseMetric metric = session.mProviderSessionMetric
323                         .getCandidatePhasePerProviderMetric();
324                 if (sessionId == -1) {
325                     sessionId = metric.getSessionIdProvider();
326                 }
327                 if (!queryReturned) {
328                     queryReturned = metric.isQueryReturned();
329                 }
330                 candidateUidList[index] = metric.getCandidateUid();
331                 candidateQueryStartTimeStampList[index] =
332                         metric.getTimestampFromReferenceStartMicroseconds(
333                                 metric.getStartQueryTimeNanoseconds());
334                 candidateQueryEndTimeStampList[index] =
335                         metric.getTimestampFromReferenceStartMicroseconds(
336                                 metric.getQueryFinishTimeNanoseconds());
337                 candidateStatusList[index] = metric.getProviderQueryStatus();
338                 candidateHasExceptionList[index] = metric.isHasException();
339                 candidateTotalEntryCountList[index] = metric.getResponseCollective()
340                         .getNumEntriesTotal();
341                 candidateCredentialEntryCountList[index] = metric.getResponseCollective()
342                         .getCountForEntry(EntryEnum.CREDENTIAL_ENTRY);
343                 candidateCredentialTypeCountList[index] = metric.getResponseCollective()
344                         .getUniqueResponseStrings().length;
345                 candidateActionEntryCountList[index] = metric.getResponseCollective()
346                         .getCountForEntry(EntryEnum.ACTION_ENTRY);
347                 candidateAuthEntryCountList[index] = metric.getResponseCollective()
348                         .getCountForEntry(EntryEnum.AUTHENTICATION_ENTRY);
349                 candidateRemoteEntryCountList[index] = metric.getResponseCollective()
350                         .getCountForEntry(EntryEnum.REMOTE_ENTRY);
351                 frameworkExceptionList[index] = metric.getFrameworkException();
352                 candidatePrimaryProviderList[index] = metric.isPrimary();
353                 index++;
354             }
355             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED,
356                     /* session_id */ sessionId,
357                     /* sequence_num */ emitSequenceId,
358                     /* query_returned */ queryReturned,
359                     /* candidate_provider_uid_list */ candidateUidList,
360                     /* candidate_provider_query_start_timestamp_microseconds */
361                     candidateQueryStartTimeStampList,
362                     /* candidate_provider_query_end_timestamp_microseconds */
363                     candidateQueryEndTimeStampList,
364                     /* candidate_provider_status */ candidateStatusList,
365                     /* candidate_provider_has_exception */ candidateHasExceptionList,
366                     /* candidate_provider_num_entries */ candidateTotalEntryCountList,
367                     /* candidate_provider_action_entry_count */ candidateActionEntryCountList,
368                     /* candidate_provider_credential_entry_count */
369                     candidateCredentialEntryCountList,
370                     /* candidate_provider_credential_entry_type_count */
371                     candidateCredentialTypeCountList,
372                     /* candidate_provider_remote_entry_count */ candidateRemoteEntryCountList,
373                     /* candidate_provider_authentication_entry_count */
374                     candidateAuthEntryCountList,
375                     /* framework_exception_per_provider */
376                     frameworkExceptionList,
377                     /* origin_specified originSpecified */
378                     initialPhaseMetric.isOriginSpecified(),
379                     /* request_unique_classtypes */
380                     initialPhaseMetric.getUniqueRequestStrings(),
381                     /* per_classtype_counts */
382                     initialPhaseMetric.getUniqueRequestCounts(),
383                     /* api_name */
384                     initialPhaseMetric.getApiName(),
385                     /* primary_candidates_indicated */
386                     candidatePrimaryProviderList
387             );
388         } catch (Exception e) {
389             Slog.w(TAG, "Unexpected error during candidate provider uid metric emit: " + e);
390         }
391     }
392 
393     /**
394      * This is useful just to record an API calls' final event, and for no other purpose.
395      *
396      * @param apiName    the api name to log
397      * @param apiStatus  the status to log
398      * @param callingUid the calling uid
399      */
logApiCalledSimpleV2(ApiName apiName, ApiStatus apiStatus, int callingUid)400     public static void logApiCalledSimpleV2(ApiName apiName, ApiStatus apiStatus,
401             int callingUid) {
402         try {
403             if (!LOG_FLAG) {
404                 return;
405             }
406             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_APIV2_CALLED,
407                     /* api_name */apiName.getMetricCode(),
408                     /* caller_uid */ callingUid,
409                     /* api_status */ apiStatus.getMetricCode());
410         } catch (Exception e) {
411             Slog.w(TAG, "Unexpected error during simple v2 metric logging: " + e);
412         }
413     }
414 
415     /**
416      * Handles the metric emit for the initial phase.
417      *
418      * @param initialPhaseMetric contains all the data for this emit
419      * @param sequenceNum        the sequence number for this api call session emit
420      */
logApiCalledInitialPhase(InitialPhaseMetric initialPhaseMetric, int sequenceNum)421     public static void logApiCalledInitialPhase(InitialPhaseMetric initialPhaseMetric,
422             int sequenceNum) {
423         try {
424             if (!LOG_FLAG) {
425                 return;
426             }
427             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_INIT_PHASE_REPORTED,
428                     /* api_name */ initialPhaseMetric.getApiName(),
429                     /* caller_uid */ initialPhaseMetric.getCallerUid(),
430                     /* session_id */ initialPhaseMetric.getSessionIdCaller(),
431                     /* sequence_num */ sequenceNum,
432                     /* initial_timestamp_reference_nanoseconds */
433                     initialPhaseMetric.getCredentialServiceStartedTimeNanoseconds(),
434                     /* count_credential_request_classtypes */
435                     initialPhaseMetric.getCountRequestClassType(),
436                     /* request_unique_classtypes */
437                     initialPhaseMetric.getUniqueRequestStrings(),
438                     /* per_classtype_counts */
439                     initialPhaseMetric.getUniqueRequestCounts(),
440                     /* origin_specified */
441                     initialPhaseMetric.isOriginSpecified(),
442                     /* autofill_session_id */
443                     initialPhaseMetric.getAutofillSessionId(),
444                     /* autofill_request_id */
445                     initialPhaseMetric.getAutofillRequestId()
446             );
447         } catch (Exception e) {
448             Slog.w(TAG, "Unexpected error during initial metric emit: " + e);
449         }
450     }
451 
452     /**
453      * A logging utility focused on track 1, where the calling app is known. This captures all
454      * aggregate information for the candidate phase.
455      *
456      * @param candidateAggregateMetric the aggregate candidate metric information collected
457      * @param sequenceNum the sequence number for this api call session emit
458      */
logApiCalledAggregateCandidate( CandidateAggregateMetric candidateAggregateMetric, int sequenceNum)459     public static void logApiCalledAggregateCandidate(
460             CandidateAggregateMetric candidateAggregateMetric,
461             int sequenceNum) {
462         try {
463             if (!LOG_FLAG) {
464                 return;
465             }
466             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_TOTAL_REPORTED,
467                     /*session_id*/ candidateAggregateMetric.getSessionIdProvider(),
468                     /*sequence_num*/ sequenceNum,
469                     /*query_returned*/ candidateAggregateMetric.isQueryReturned(),
470                     /*num_query_providers*/ candidateAggregateMetric.getNumProviders(),
471                     /*min_query_start_timestamp_microseconds*/
472                     getMetricTimestampDifferenceMicroseconds(
473                             candidateAggregateMetric.getMinProviderTimestampNanoseconds(),
474                             candidateAggregateMetric.getServiceBeganTimeNanoseconds()),
475                     /*max_query_end_timestamp_microseconds*/
476                     getMetricTimestampDifferenceMicroseconds(
477                             candidateAggregateMetric.getMaxProviderTimestampNanoseconds(),
478                             candidateAggregateMetric.getServiceBeganTimeNanoseconds()),
479                     /*query_response_unique_classtypes*/
480                     candidateAggregateMetric.getAggregateCollectiveQuery()
481                             .getUniqueResponseStrings(),
482                     /*query_per_classtype_counts*/
483                     candidateAggregateMetric.getAggregateCollectiveQuery()
484                             .getUniqueResponseCounts(),
485                     /*query_unique_entries*/
486                     candidateAggregateMetric.getAggregateCollectiveQuery()
487                             .getUniqueEntries(),
488                     /*query_per_entry_counts*/
489                     candidateAggregateMetric.getAggregateCollectiveQuery()
490                             .getUniqueEntryCounts(),
491                     /*query_total_candidate_failure*/
492                     candidateAggregateMetric.getTotalQueryFailures(),
493                     /*query_framework_exception_unique_classtypes*/
494                     candidateAggregateMetric.getUniqueExceptionStringsQuery(),
495                     /*query_per_exception_classtype_counts*/
496                     candidateAggregateMetric.getUniqueExceptionCountsQuery(),
497                     /*auth_response_unique_classtypes*/
498                     candidateAggregateMetric.getAggregateCollectiveAuth()
499                             .getUniqueResponseStrings(),
500                     /*auth_per_classtype_counts*/
501                     candidateAggregateMetric.getAggregateCollectiveAuth()
502                             .getUniqueResponseCounts(),
503                     /*auth_unique_entries*/
504                     candidateAggregateMetric.getAggregateCollectiveAuth()
505                             .getUniqueEntries(),
506                     /*auth_per_entry_counts*/
507                     candidateAggregateMetric.getAggregateCollectiveAuth()
508                             .getUniqueEntryCounts(),
509                     /*auth_total_candidate_failure*/
510                     candidateAggregateMetric.getTotalAuthFailures(),
511                     /*auth_framework_exception_unique_classtypes*/
512                     candidateAggregateMetric.getUniqueExceptionStringsAuth(),
513                     /*auth_per_exception_classtype_counts*/
514                     candidateAggregateMetric.getUniqueExceptionCountsAuth(),
515                     /*num_auth_clicks*/
516                     candidateAggregateMetric.getNumAuthEntriesTapped(),
517                     /*auth_returned*/
518                     candidateAggregateMetric.isAuthReturned()
519             );
520         } catch (Exception e) {
521             Slog.w(TAG, "Unexpected error during total candidate metric logging: " + e);
522         }
523     }
524 
525     /**
526      * A logging utility used primarily for the final phase of the current metric setup for track 1.
527      *
528      * @param finalPhaseMetric     the coalesced data of the chosen provider
529      * @param browsingPhaseMetrics the coalesced data of the browsing phase
530      * @param apiStatus            the final status of this particular api call
531      * @param emitSequenceId       an emitted sequence id for the current session
532      */
logApiCalledNoUidFinal(ChosenProviderFinalPhaseMetric finalPhaseMetric, List<CandidateBrowsingPhaseMetric> browsingPhaseMetrics, int apiStatus, int emitSequenceId)533     public static void logApiCalledNoUidFinal(ChosenProviderFinalPhaseMetric finalPhaseMetric,
534             List<CandidateBrowsingPhaseMetric> browsingPhaseMetrics, int apiStatus,
535             int emitSequenceId) {
536         try {
537             if (!LOG_FLAG) {
538                 return;
539             }
540             int browsedSize = browsingPhaseMetrics.size();
541             int[] browsedClickedEntries = new int[browsedSize];
542             int[] browsedProviderUid = new int[browsedSize];
543             int index = 0;
544             for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) {
545                 browsedClickedEntries[index] = metric.getEntryEnum();
546                 browsedProviderUid[index] = DEFAULT_INT_32;
547                 index++;
548             }
549             FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINALNOUID_REPORTED,
550                     /* session_id */ finalPhaseMetric.getSessionIdCaller(),
551                     /* sequence_num */ emitSequenceId,
552                     /* ui_returned_final_start */ finalPhaseMetric.isUiReturned(),
553                     /* chosen_provider_query_start_timestamp_microseconds */
554                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
555                             .getQueryStartTimeNanoseconds()),
556                     /* chosen_provider_query_end_timestamp_microseconds */
557                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
558                             .getQueryEndTimeNanoseconds()),
559                     /* chosen_provider_ui_invoked_timestamp_microseconds */
560                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
561                             .getUiCallStartTimeNanoseconds()),
562                     /* chosen_provider_ui_finished_timestamp_microseconds */
563                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
564                             .getUiCallEndTimeNanoseconds()),
565                     /* chosen_provider_finished_timestamp_microseconds */
566                     finalPhaseMetric.getTimestampFromReferenceStartMicroseconds(finalPhaseMetric
567                             .getFinalFinishTimeNanoseconds()),
568                     /* chosen_provider_status */ finalPhaseMetric.getChosenProviderStatus(),
569                     /* chosen_provider_has_exception */ finalPhaseMetric.isHasException(),
570                     /* unique_entries */
571                     finalPhaseMetric.getResponseCollective().getUniqueEntries(),
572                     /* per_entry_counts */
573                     finalPhaseMetric.getResponseCollective().getUniqueEntryCounts(),
574                     /* unique_response_classtypes */
575                     finalPhaseMetric.getResponseCollective().getUniqueResponseStrings(),
576                     /* per_classtype_counts */
577                     finalPhaseMetric.getResponseCollective().getUniqueResponseCounts(),
578                     /* framework_exception_unique_classtype */
579                     finalPhaseMetric.getFrameworkException(),
580                     /* clicked_entries */ browsedClickedEntries,
581                     /* provider_of_clicked_entry */ browsedProviderUid,
582                     /* api_status */ apiStatus,
583                     /* primary_indicated */ finalPhaseMetric.isPrimary(),
584                     /* oem_credential_manager_ui_uid */ finalPhaseMetric.getOemUiUid(),
585                     /* fallback_credential_manager_ui_uid */ finalPhaseMetric.getFallbackUiUid(),
586                     /* oem_ui_usage_status */ finalPhaseMetric.getOemUiUsageStatus()
587             );
588         } catch (Exception e) {
589             Slog.w(TAG, "Unexpected error during final no uid metric logging: " + e);
590         }
591     }
592 
593 }
594