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