1 /*
2  * Copyright (C) 2019 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;
18 
19 import android.content.Context;
20 import android.hardware.biometrics.BiometricConstants;
21 import android.hardware.biometrics.BiometricsProtoEnums;
22 import android.hardware.face.FaceManager;
23 import android.util.Slog;
24 import android.util.StatsLog;
25 
26 /**
27  * Abstract class that adds logging functionality to the ClientMonitor classes.
28  */
29 public abstract class LoggableMonitor {
30 
31     public static final String TAG = "BiometricStats";
32     public static final boolean DEBUG = false;
33 
34     private long mFirstAcquireTimeMs;
35 
getFirstAcquireTimeMs()36     protected long getFirstAcquireTimeMs() {
37         return mFirstAcquireTimeMs;
38     }
39 
40     /**
41      * Only valid for AuthenticationClient.
42      * @return true if the client is authenticating for a crypto operation.
43      */
isCryptoOperation()44     protected boolean isCryptoOperation() {
45         return false;
46     }
47 
48     /**
49      * @return One of {@link BiometricsProtoEnums} MODALITY_* constants.
50      */
statsModality()51     protected abstract int statsModality();
52 
53     /**
54      * Action == enroll, authenticate, remove, enumerate.
55      * @return One of {@link BiometricsProtoEnums} ACTION_* constants.
56      */
statsAction()57     protected abstract int statsAction();
58 
59     /**
60      * Only matters for AuthenticationClient. Should only be overridden in
61      * {@link BiometricServiceBase}, which determines if a client is for BiometricPrompt, Keyguard,
62      * etc.
63      * @return one of {@link BiometricsProtoEnums} CLIENT_* constants.
64      */
statsClient()65     protected int statsClient() {
66         return BiometricsProtoEnums.CLIENT_UNKNOWN;
67     }
68 
logOnAcquired(Context context, int acquiredInfo, int vendorCode, int targetUserId)69     protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
70             int targetUserId) {
71         if (statsModality() == BiometricsProtoEnums.MODALITY_FACE) {
72             if (acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
73                 mFirstAcquireTimeMs = System.currentTimeMillis();
74             }
75         } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
76             if (mFirstAcquireTimeMs == 0) {
77                 mFirstAcquireTimeMs = System.currentTimeMillis();
78             }
79         }
80         if (DEBUG) {
81             Slog.v(TAG, "Acquired! Modality: " + statsModality()
82                     + ", User: " + targetUserId
83                     + ", IsCrypto: " + isCryptoOperation()
84                     + ", Action: " + statsAction()
85                     + ", Client: " + statsClient()
86                     + ", AcquiredInfo: " + acquiredInfo
87                     + ", VendorCode: " + vendorCode);
88         }
89         StatsLog.write(StatsLog.BIOMETRIC_ACQUIRED,
90                 statsModality(),
91                 targetUserId,
92                 isCryptoOperation(),
93                 statsAction(),
94                 statsClient(),
95                 acquiredInfo,
96                 vendorCode,
97                 Utils.isDebugEnabled(context, targetUserId));
98     }
99 
logOnError(Context context, int error, int vendorCode, int targetUserId)100     protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) {
101 
102         final long latency = mFirstAcquireTimeMs != 0
103                 ? (System.currentTimeMillis() - mFirstAcquireTimeMs) : -1;
104 
105         if (DEBUG) {
106             Slog.v(TAG, "Error! Modality: " + statsModality()
107                     + ", User: " + targetUserId
108                     + ", IsCrypto: " + isCryptoOperation()
109                     + ", Action: " + statsAction()
110                     + ", Client: " + statsClient()
111                     + ", Error: " + error
112                     + ", VendorCode: " + vendorCode
113                     + ", Latency: " + latency);
114         } else {
115             Slog.v(TAG, "Error latency: " + latency);
116         }
117         StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
118                 statsModality(),
119                 targetUserId,
120                 isCryptoOperation(),
121                 statsAction(),
122                 statsClient(),
123                 error,
124                 vendorCode,
125                 Utils.isDebugEnabled(context, targetUserId),
126                 latency);
127     }
128 
logOnAuthenticated(Context context, boolean authenticated, boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt)129     protected final void logOnAuthenticated(Context context, boolean authenticated,
130             boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
131         int authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
132         if (!authenticated) {
133             authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
134         } else {
135             // Authenticated
136             if (isBiometricPrompt && requireConfirmation) {
137                 authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
138             } else {
139                 authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
140             }
141         }
142 
143         // Only valid if we have a first acquired time, otherwise set to -1
144         final long latency = mFirstAcquireTimeMs != 0
145                 ? (System.currentTimeMillis() - mFirstAcquireTimeMs)
146                 : -1;
147 
148         if (DEBUG) {
149             Slog.v(TAG, "Authenticated! Modality: " + statsModality()
150                     + ", User: " + targetUserId
151                     + ", IsCrypto: " + isCryptoOperation()
152                     + ", Client: " + statsClient()
153                     + ", RequireConfirmation: " + requireConfirmation
154                     + ", State: " + authState
155                     + ", Latency: " + latency);
156         } else {
157             Slog.v(TAG, "Authentication latency: " + latency);
158         }
159 
160         StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
161                 statsModality(),
162                 targetUserId,
163                 isCryptoOperation(),
164                 statsClient(),
165                 requireConfirmation,
166                 authState,
167                 latency,
168                 Utils.isDebugEnabled(context, targetUserId));
169     }
170 
logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful)171     protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
172         if (DEBUG) {
173             Slog.v(TAG, "Enrolled! Modality: " + statsModality()
174                     + ", User: " + targetUserId
175                     + ", Client: " + statsClient()
176                     + ", Latency: " + latency
177                     + ", Success: " + enrollSuccessful);
178         } else {
179             Slog.v(TAG, "Enroll latency: " + latency);
180         }
181 
182         StatsLog.write(StatsLog.BIOMETRIC_ENROLLED,
183                 statsModality(),
184                 targetUserId,
185                 latency,
186                 enrollSuccessful);
187     }
188 
189 }
190