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