/**
   * Starts the biometric unlock if it should be started based on a number of factors. If it should
   * not be started, it either goes to the back up, or remains showing to prepare for it being
   * started later.
   */
  private void maybeStartBiometricUnlock() {
    log("maybeStartBiometricUnlock() is called.");
    if (mBiometricUnlock != null) {
      KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
      final boolean backupIsTimedOut =
          (monitor.getFailedUnlockAttempts() >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
      final boolean mediaPlaying =
          AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
              || AudioSystem.isStreamActive(AudioSystem.STREAM_ALARM, 0);

      boolean isBouncerVisibleToUser;
      synchronized (mIsBouncerVisibleToUserLock) {
        isBouncerVisibleToUser = mIsBouncerVisibleToUser;
      }

      // Don't start it if the bouncer is not showing, but keep this view up because we want
      // it here and ready for when the bouncer does show.
      if (!isBouncerVisibleToUser) {
        if (mediaPlaying) {
          log(
              "maybeStartBiometricUnlock() - isBouncerVisibleToUser is false"
                  + " && mediaPlaying is true, call mBiometricUnlock.stopAndShowBackup()");
          mBiometricUnlock.stopAndShowBackup();
        } else {
          log(
              "maybeStartBiometricUnlock() - isBouncerVisibleToUser is false,"
                  + " call mBiometricUnlock.stop()");
          mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt.
        }
        return;
      }

      // Although these same conditions are handled in KeyguardSecurityModel, they are still
      // necessary here.  When a tablet is rotated 90 degrees, a configuration change is
      // triggered and everything is torn down and reconstructed.  That means
      // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even
      // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed.
      // However, for a 180 degree rotation, no configuration change is triggered, so only
      // the logic here is capable of suppressing Face Unlock.
      if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
          && monitor.isAlternateUnlockEnabled()
          && !monitor.getMaxBiometricUnlockAttemptsReached()
          && !backupIsTimedOut
          && !mediaPlaying) {
        mBiometricUnlock.start();
      } else {
        log("maybeStartBiometricUnlock() - call stopAndShowBackup()");
        mBiometricUnlock.stopAndShowBackup();
      }
    }
  }
    /** Configures echo cancellation and noise suppression effects. */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void configureEffects() {
      if (!AndroidUtils.hasAPI(16)) return;

      AudioSystem audioSystem =
          AudioSystem.getAudioSystem(AudioSystem.LOCATOR_PROTOCOL_AUDIORECORD);

      // Creates echo canceler if available
      if (AcousticEchoCanceler.isAvailable()) {
        AcousticEchoCanceler echoCanceller =
            AcousticEchoCanceler.create(audioRecord.getAudioSessionId());
        if (echoCanceller != null) {
          echoCanceller.setEnableStatusListener(this);
          echoCanceller.setEnabled(audioSystem.isEchoCancel());
          logger.info("Echo cancellation: " + echoCanceller.getEnabled());
        }
      }

      // Automatic gain control
      if (AutomaticGainControl.isAvailable()) {
        AutomaticGainControl agc = AutomaticGainControl.create(audioRecord.getAudioSessionId());
        if (agc != null) {
          agc.setEnableStatusListener(this);
          agc.setEnabled(audioSystem.isAutomaticGainControl());
          logger.info("Auto gain control: " + agc.getEnabled());
        }
      }

      // Creates noise suppressor if available
      if (NoiseSuppressor.isAvailable()) {
        NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId());
        if (noiseSuppressor != null) {
          noiseSuppressor.setEnableStatusListener(this);
          noiseSuppressor.setEnabled(audioSystem.isDenoise());
          logger.info("Noise suppressor: " + noiseSuppressor.getEnabled());
        }
      }
    }