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