1 /*
2  * Copyright (C) 2022 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.biometrics.log;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.biometrics.BiometricsProtoEnums;
22 import android.hardware.biometrics.IBiometricContextListener;
23 import android.hardware.biometrics.common.AuthenticateReason;
24 import android.hardware.biometrics.common.OperationContext;
25 import android.hardware.biometrics.common.OperationReason;
26 import android.hardware.biometrics.common.WakeReason;
27 import android.util.Slog;
28 import android.view.Surface;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.util.FrameworkStatsLog;
32 
33 import java.util.stream.Stream;
34 
35 /**
36  * Wrapper for {@link FrameworkStatsLog} to isolate the testable parts.
37  */
38 public class BiometricFrameworkStatsLogger {
39 
40     private static final String TAG = "BiometricFrameworkStatsLogger";
41 
42     private static final BiometricFrameworkStatsLogger sInstance =
43             new BiometricFrameworkStatsLogger();
44 
BiometricFrameworkStatsLogger()45     private BiometricFrameworkStatsLogger() {}
46 
47     /** Shared instance. */
getInstance()48     public static BiometricFrameworkStatsLogger getInstance() {
49         return sInstance;
50     }
51 
52     /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
acquired(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, int acquiredInfo, int vendorCode, int targetUserId)53     public void acquired(OperationContextExt operationContext,
54             int statsModality, int statsAction, int statsClient, boolean isDebug,
55             int acquiredInfo, int vendorCode, int targetUserId) {
56         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
57                 statsModality,
58                 targetUserId,
59                 operationContext.isCrypto(),
60                 statsAction,
61                 statsClient,
62                 acquiredInfo,
63                 vendorCode,
64                 isDebug,
65                 -1 /* sensorId */,
66                 operationContext.getId(),
67                 sessionType(operationContext.getReason()),
68                 operationContext.isAod(),
69                 operationContext.isDisplayOn(),
70                 operationContext.getDockState(),
71                 orientationType(operationContext.getOrientation()),
72                 foldType(operationContext.getFoldState()),
73                 operationContext.getOrderAndIncrement(),
74                 toProtoWakeReason(operationContext));
75     }
76 
77     /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
authenticate(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux)78     public void authenticate(OperationContextExt operationContext,
79             int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
80             int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux) {
81         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
82                 statsModality,
83                 targetUserId,
84                 operationContext.isCrypto(),
85                 statsClient,
86                 requireConfirmation,
87                 authState,
88                 sanitizeLatency(latency),
89                 isDebug,
90                 -1 /* sensorId */,
91                 ambientLightLux,
92                 operationContext.getId(),
93                 sessionType(operationContext.getReason()),
94                 operationContext.isAod(),
95                 operationContext.isDisplayOn(),
96                 operationContext.getDockState(),
97                 orientationType(operationContext.getOrientation()),
98                 foldType(operationContext.getFoldState()),
99                 operationContext.getOrderAndIncrement(),
100                 toProtoWakeReason(operationContext),
101                 toProtoWakeReasonDetails(operationContext));
102     }
103 
104     /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
authenticate(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int authState, boolean requireConfirmation, int targetUserId, ALSProbe alsProbe)105     public void authenticate(OperationContextExt operationContext,
106             int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
107             int authState, boolean requireConfirmation, int targetUserId, ALSProbe alsProbe) {
108         alsProbe.awaitNextLux((ambientLightLux) -> {
109             authenticate(operationContext, statsModality, statsAction, statsClient, isDebug,
110                     latency, authState, requireConfirmation, targetUserId, ambientLightLux);
111         }, null /* handler */);
112     }
113 
114     /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
enroll(int statsModality, int statsAction, int statsClient, int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux)115     public void enroll(int statsModality, int statsAction, int statsClient,
116             int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
117         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
118                 statsModality,
119                 targetUserId,
120                 sanitizeLatency(latency),
121                 enrollSuccessful,
122                 -1, /* sensorId */
123                 ambientLightLux);
124     }
125 
126     /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
error(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int error, int vendorCode, int targetUserId)127     public void error(OperationContextExt operationContext,
128             int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
129             int error, int vendorCode, int targetUserId) {
130         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
131                 statsModality,
132                 targetUserId,
133                 operationContext.isCrypto(),
134                 statsAction,
135                 statsClient,
136                 error,
137                 vendorCode,
138                 isDebug,
139                 sanitizeLatency(latency),
140                 -1 /* sensorId */,
141                 operationContext.getId(),
142                 sessionType(operationContext.getReason()),
143                 operationContext.isAod(),
144                 operationContext.isDisplayOn(),
145                 operationContext.getDockState(),
146                 orientationType(operationContext.getOrientation()),
147                 foldType(operationContext.getFoldState()),
148                 operationContext.getOrderAndIncrement(),
149                 toProtoWakeReason(operationContext),
150                 toProtoWakeReasonDetails(operationContext));
151     }
152 
153     @VisibleForTesting
toProtoWakeReasonDetails(@onNull OperationContextExt operationContext)154     static int[] toProtoWakeReasonDetails(@NonNull OperationContextExt operationContext) {
155         final OperationContext ctx = operationContext.toAidlContext();
156         return Stream.of(toProtoWakeReasonDetails(ctx.authenticateReason))
157                 .mapToInt(i -> i)
158                 .filter(i -> i != BiometricsProtoEnums.DETAILS_UNKNOWN)
159                 .toArray();
160     }
161 
162     @VisibleForTesting
toProtoWakeReason(@onNull OperationContextExt operationContext)163     static int toProtoWakeReason(@NonNull OperationContextExt operationContext) {
164         @WakeReason final int reason = operationContext.getWakeReason();
165         switch (reason) {
166             case WakeReason.POWER_BUTTON:
167                 return BiometricsProtoEnums.WAKE_REASON_POWER_BUTTON;
168             case WakeReason.GESTURE:
169                 return BiometricsProtoEnums.WAKE_REASON_GESTURE;
170             case WakeReason.WAKE_KEY:
171                 return BiometricsProtoEnums.WAKE_REASON_WAKE_KEY;
172             case WakeReason.WAKE_MOTION:
173                 return BiometricsProtoEnums.WAKE_REASON_WAKE_MOTION;
174             case WakeReason.LID:
175                 return BiometricsProtoEnums.WAKE_REASON_LID;
176             case WakeReason.DISPLAY_GROUP_ADDED:
177                 return BiometricsProtoEnums.WAKE_REASON_DISPLAY_GROUP_ADDED;
178             case WakeReason.TAP:
179                 return BiometricsProtoEnums.WAKE_REASON_TAP;
180             case WakeReason.LIFT:
181                 return BiometricsProtoEnums.WAKE_REASON_LIFT;
182             case WakeReason.BIOMETRIC:
183                 return BiometricsProtoEnums.WAKE_REASON_BIOMETRIC;
184             default:
185                 return BiometricsProtoEnums.WAKE_REASON_UNKNOWN;
186         }
187     }
188 
toProtoWakeReasonDetails(@ullable AuthenticateReason reason)189     private static int toProtoWakeReasonDetails(@Nullable AuthenticateReason reason) {
190         if (reason != null) {
191             switch (reason.getTag()) {
192                 case AuthenticateReason.faceAuthenticateReason:
193                     return toProtoWakeReasonDetailsFromFace(reason.getFaceAuthenticateReason());
194             }
195         }
196         return BiometricsProtoEnums.DETAILS_UNKNOWN;
197     }
198 
toProtoWakeReasonDetailsFromFace(@uthenticateReason.Face int reason)199     private static int toProtoWakeReasonDetailsFromFace(@AuthenticateReason.Face int reason) {
200         switch (reason) {
201             case AuthenticateReason.Face.STARTED_WAKING_UP:
202                 return BiometricsProtoEnums.DETAILS_FACE_STARTED_WAKING_UP;
203             case AuthenticateReason.Face.PRIMARY_BOUNCER_SHOWN:
204                 return BiometricsProtoEnums.DETAILS_FACE_PRIMARY_BOUNCER_SHOWN;
205             case AuthenticateReason.Face.ASSISTANT_VISIBLE:
206                 return BiometricsProtoEnums.DETAILS_FACE_ASSISTANT_VISIBLE;
207             case AuthenticateReason.Face.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN:
208                 return BiometricsProtoEnums.DETAILS_FACE_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN;
209             case AuthenticateReason.Face.NOTIFICATION_PANEL_CLICKED:
210                 return BiometricsProtoEnums.DETAILS_FACE_NOTIFICATION_PANEL_CLICKED;
211             case AuthenticateReason.Face.OCCLUDING_APP_REQUESTED:
212                 return BiometricsProtoEnums.DETAILS_FACE_OCCLUDING_APP_REQUESTED;
213             case AuthenticateReason.Face.PICK_UP_GESTURE_TRIGGERED:
214                 return BiometricsProtoEnums.DETAILS_FACE_PICK_UP_GESTURE_TRIGGERED;
215             case AuthenticateReason.Face.QS_EXPANDED:
216                 return BiometricsProtoEnums.DETAILS_FACE_QS_EXPANDED;
217             case AuthenticateReason.Face.SWIPE_UP_ON_BOUNCER:
218                 return BiometricsProtoEnums.DETAILS_FACE_SWIPE_UP_ON_BOUNCER;
219             case AuthenticateReason.Face.UDFPS_POINTER_DOWN:
220                 return BiometricsProtoEnums.DETAILS_FACE_UDFPS_POINTER_DOWN;
221             default:
222                 return BiometricsProtoEnums.DETAILS_UNKNOWN;
223         }
224     }
225 
226     /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
reportUnknownTemplateEnrolledHal(int statsModality)227     public void reportUnknownTemplateEnrolledHal(int statsModality) {
228         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
229                 statsModality,
230                 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
231                 -1 /* sensorId */);
232     }
233 
234     /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
reportUnknownTemplateEnrolledFramework(int statsModality)235     public void reportUnknownTemplateEnrolledFramework(int statsModality) {
236         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
237                 statsModality,
238                 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
239                 -1 /* sensorId */);
240     }
241 
sanitizeLatency(long latency)242     private long sanitizeLatency(long latency) {
243         if (latency < 0) {
244             Slog.w(TAG, "found a negative latency : " + latency);
245             return -1;
246         }
247         return latency;
248     }
249 
sessionType(@perationReason byte reason)250     private static int sessionType(@OperationReason byte reason) {
251         if (reason == OperationReason.BIOMETRIC_PROMPT) {
252             return BiometricsProtoEnums.SESSION_TYPE_BIOMETRIC_PROMPT;
253         }
254         if (reason == OperationReason.KEYGUARD) {
255             return BiometricsProtoEnums.SESSION_TYPE_KEYGUARD_ENTRY;
256         }
257         return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN;
258     }
259 
orientationType(@urface.Rotation int rotation)260     private static int orientationType(@Surface.Rotation int rotation) {
261         switch (rotation) {
262             case Surface.ROTATION_0:
263                 return BiometricsProtoEnums.ORIENTATION_0;
264             case Surface.ROTATION_90:
265                 return BiometricsProtoEnums.ORIENTATION_90;
266             case Surface.ROTATION_180:
267                 return BiometricsProtoEnums.ORIENTATION_180;
268             case Surface.ROTATION_270:
269                 return BiometricsProtoEnums.ORIENTATION_270;
270         }
271         return BiometricsProtoEnums.ORIENTATION_UNKNOWN;
272     }
273 
foldType(int foldType)274     private static int foldType(int foldType) {
275         switch (foldType) {
276             case IBiometricContextListener.FoldState.FULLY_CLOSED:
277                 return BiometricsProtoEnums.FOLD_CLOSED;
278             case IBiometricContextListener.FoldState.FULLY_OPENED:
279                 return BiometricsProtoEnums.FOLD_OPEN;
280             case IBiometricContextListener.FoldState.HALF_OPENED:
281                 return BiometricsProtoEnums.FOLD_HALF_OPEN;
282         }
283         return BiometricsProtoEnums.FOLD_UNKNOWN;
284     }
285 }
286