content: /*
   * Copyright (C) 2019 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.biometrics;
  
  import android.content.Context;
  import android.hardware.biometrics.BiometricConstants;
  import android.hardware.biometrics.BiometricsProtoEnums;
  import android.hardware.face.FaceManager;
  import android.util.Slog;
  import android.util.StatsLog;
  
  /**
   * Abstract class that adds logging functionality to the ClientMonitor classes.
   */
  public abstract class LoggableMonitor {
  
      public static final String TAG = "BiometricStats";
      public static final boolean DEBUG = false;
  
      private long mFirstAcquireTimeMs;
  
      protected long getFirstAcquireTimeMs() {
          return mFirstAcquireTimeMs;
      }
  
      /**
       * Only valid for AuthenticationClient.
       * @return true if the client is authenticating for a crypto operation.
       */
      protected boolean isCryptoOperation() {
          return false;
      }
  
      /**
       * @return One of {@link BiometricsProtoEnums} MODALITY_* constants.
       */
      protected abstract int statsModality();
  
      /**
       * Action == enroll, authenticate, remove, enumerate.
       * @return One of {@link BiometricsProtoEnums} ACTION_* constants.
       */
      protected abstract int statsAction();
  
      /**
       * Only matters for AuthenticationClient. Should only be overridden in
       * {@link BiometricServiceBase}, which determines if a client is for BiometricPrompt, Keyguard,
       * etc.
       * @return one of {@link BiometricsProtoEnums} CLIENT_* constants.
       */
      protected int statsClient() {
          return BiometricsProtoEnums.CLIENT_UNKNOWN;
      }
  
      protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
              int targetUserId) {
          if (statsModality() == BiometricsProtoEnums.MODALITY_FACE) {
              if (acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
                  mFirstAcquireTimeMs = System.currentTimeMillis();
              }
          } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
              if (mFirstAcquireTimeMs == 0) {
                  mFirstAcquireTimeMs = System.currentTimeMillis();
              }
          }
          if (DEBUG) {
              Slog.v(TAG, "Acquired! Modality: " + statsModality()
                      + ", User: " + targetUserId
                      + ", IsCrypto: " + isCryptoOperation()
                      + ", Action: " + statsAction()
                      + ", Client: " + statsClient()
                      + ", AcquiredInfo: " + acquiredInfo
                      + ", VendorCode: " + vendorCode);
          }
          StatsLog.write(StatsLog.BIOMETRIC_ACQUIRED,
                  statsModality(),
                  targetUserId,
                  isCryptoOperation(),
                  statsAction(),
                  statsClient(),
                  acquiredInfo,
                  vendorCode,
                  Utils.isDebugEnabled(context, targetUserId));
      }
  
      protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) {
  
          final long latency = mFirstAcquireTimeMs != 0
                  ? (System.currentTimeMillis() - mFirstAcquireTimeMs) : -1;
  
          if (DEBUG) {
              Slog.v(TAG, "Error! Modality: " + statsModality()
                      + ", User: " + targetUserId
                      + ", IsCrypto: " + isCryptoOperation()
                      + ", Action: " + statsAction()
                      + ", Client: " + statsClient()
                      + ", Error: " + error
                      + ", VendorCode: " + vendorCode
                      + ", Latency: " + latency);
          } else {
              Slog.v(TAG, "Error latency: " + latency);
          }
          StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
                  statsModality(),
                  targetUserId,
                  isCryptoOperation(),
                  statsAction(),
                  statsClient(),
                  error,
                  vendorCode,
                  Utils.isDebugEnabled(context, targetUserId),
                  latency);
      }
  
      protected final void logOnAuthenticated(Context context, boolean authenticated,
              boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
          int authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
          if (!authenticated) {
              authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
          } else {
              // Authenticated
              if (isBiometricPrompt && requireConfirmation) {
                  authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
              } else {
                  authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
              }
          }
  
          // Only valid if we have a first acquired time, otherwise set to -1
          final long latency = mFirstAcquireTimeMs != 0
                  ? (System.currentTimeMillis() - mFirstAcquireTimeMs)
                  : -1;
  
          if (DEBUG) {
              Slog.v(TAG, "Authenticated! Modality: " + statsModality()
                      + ", User: " + targetUserId
                      + ", IsCrypto: " + isCryptoOperation()
                      + ", Client: " + statsClient()
                      + ", RequireConfirmation: " + requireConfirmation
                      + ", State: " + authState
                      + ", Latency: " + latency);
          } else {
              Slog.v(TAG, "Authentication latency: " + latency);
          }
  
          StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
                  statsModality(),
                  targetUserId,
                  isCryptoOperation(),
                  statsClient(),
                  requireConfirmation,
                  authState,
                  latency,
                  Utils.isDebugEnabled(context, targetUserId));
      }
  
      protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
          if (DEBUG) {
              Slog.v(TAG, "Enrolled! Modality: " + statsModality()
                      + ", User: " + targetUserId
                      + ", Client: " + statsClient()
                      + ", Latency: " + latency
                      + ", Success: " + enrollSuccessful);
          } else {
              Slog.v(TAG, "Enroll latency: " + latency);
          }
  
          StatsLog.write(StatsLog.BIOMETRIC_ENROLLED,
                  statsModality(),
                  targetUserId,
                  latency,
                  enrollSuccessful);
      }
  
  }
  