gt:         if (flipper != null) {
context: /*
   * Copyright (C) 2014 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.keyguard;
  
  import android.app.Activity;
  import android.app.AlertDialog;
  import android.app.admin.DevicePolicyManager;
  import android.content.Context;
  import android.os.UserHandle;
  import android.os.UserManager;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.util.Slog;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.WindowManager;
  import android.widget.FrameLayout;
  
  import com.android.internal.widget.LockPatternUtils;
  import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
  
  public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
      private static final boolean DEBUG = KeyguardConstants.DEBUG;
      private static final String TAG = "KeyguardSecurityView";
  
      private static final int USER_TYPE_PRIMARY = 1;
      private static final int USER_TYPE_WORK_PROFILE = 2;
      private static final int USER_TYPE_SECONDARY_USER = 3;
  
      private KeyguardSecurityModel mSecurityModel;
      private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
      private LockPatternUtils mLockPatternUtils;
  
      private KeyguardSecurityViewFlipper mSecurityViewFlipper;
      private boolean mIsVerifyUnlockOnly;
      private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
      private boolean mIsBouncing;
      private SecurityCallback mSecurityCallback;
  
      private final KeyguardUpdateMonitor mUpdateMonitor;
  
      // Used to notify the container when something interesting happens.
      public interface SecurityCallback {
          public boolean dismiss(boolean authenticated);
          public void userActivity();
          public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
          public void finish();
      }
  
      public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public KeyguardSecurityContainer(Context context) {
          this(context, null, 0);
      }
  
      public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          mSecurityModel = new KeyguardSecurityModel(context);
          mLockPatternUtils = new LockPatternUtils(context);
          mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
      }
  
      public void setSecurityCallback(SecurityCallback callback) {
          mSecurityCallback = callback;
      }
  
      @Override
      public void onResume(int reason) {
          if (mCurrentSecuritySelection != SecurityMode.None) {
              getSecurityView(mCurrentSecuritySelection).onResume(reason);
          }
      }
  
      @Override
      public void onPause() {
          if (mCurrentSecuritySelection != SecurityMode.None) {
              getSecurityView(mCurrentSecuritySelection).onPause();
          }
      }
  
      public void startAppearAnimation() {
          if (mCurrentSecuritySelection != SecurityMode.None) {
              getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
          }
      }
  
      public boolean startDisappearAnimation(Runnable onFinishRunnable) {
          if (mCurrentSecuritySelection != SecurityMode.None) {
              return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
                      onFinishRunnable);
          }
          return false;
      }
  
      void updateSecurityViews(boolean isBouncing) {
          int children = mSecurityViewFlipper.getChildCount();
          for (int i = 0; i < children; i++) {
              updateSecurityView(mSecurityViewFlipper.getChildAt(i), isBouncing);
          }
      }
  
      public void announceCurrentSecurityMethod() {
          View v = (View) getSecurityView(mCurrentSecuritySelection);
          if (v != null) {
              v.announceForAccessibility(v.getContentDescription());
          }
      }
  
      public CharSequence getCurrentSecurityModeContentDescription() {
          View v = (View) getSecurityView(mCurrentSecuritySelection);
          if (v != null) {
              return v.getContentDescription();
          }
          return "";
      }
  
      private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
          final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
          KeyguardSecurityView view = null;
          final int children = mSecurityViewFlipper.getChildCount();
          for (int child = 0; child < children; child++) {
              if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
                  view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
                  break;
              }
          }
          int layoutId = getLayoutIdFor(securityMode);
          if (view == null && layoutId != 0) {
              final LayoutInflater inflater = LayoutInflater.from(mContext);
              if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
              View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
              mSecurityViewFlipper.addView(v);
              updateSecurityView(v, mIsBouncing);
              view = (KeyguardSecurityView)v;
          }
  
          return view;
      }
  
      private void updateSecurityView(View view, boolean isBouncing) {
          mIsBouncing = isBouncing;
          if (view instanceof KeyguardSecurityView) {
              KeyguardSecurityView ksv = (KeyguardSecurityView) view;
              ksv.setKeyguardCallback(mCallback);
              ksv.setLockPatternUtils(mLockPatternUtils);
              if (isBouncing) {
                  ksv.showBouncer(0);
              } else {
                  ksv.hideBouncer(0);
              }
          } else {
              Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
          }
      }
  
      protected void onFinishInflate() {
          mSecurityViewFlipper = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
          mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
      }
  
      public void setLockPatternUtils(LockPatternUtils utils) {
          mLockPatternUtils = utils;
          mSecurityModel.setLockPatternUtils(utils);
          mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
      }
  
      private void showDialog(String title, String message) {
          final AlertDialog dialog = new AlertDialog.Builder(mContext)
              .setTitle(title)
              .setMessage(message)
              .setNeutralButton(R.string.ok, null)
              .create();
          if (!(mContext instanceof Activity)) {
              dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
          }
          dialog.show();
      }
  
      private void showTimeoutDialog() {
          int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
          int messageId = 0;
  
          switch (mSecurityModel.getSecurityMode()) {
              case Pattern:
                  messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
                  break;
              case PIN:
                  messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
                  break;
              case Password:
                  messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
                  break;
              // These don't have timeout dialogs.
              case Account:
              case Biometric:
              case Invalid:
              case None:
              case SimPin:
              case SimPuk:
                  break;
          }
  
          if (messageId != 0) {
              final String message = mContext.getString(messageId,
                      KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
                      timeoutInSeconds);
              showDialog(null, message);
          }
      }
  
      private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
          String message = null;
          switch (userType) {
              case USER_TYPE_PRIMARY:
                  message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
                          attempts, remaining);
                  break;
              case USER_TYPE_SECONDARY_USER:
                  message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
                          attempts, remaining);
                  break;
              case USER_TYPE_WORK_PROFILE:
                  message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
                          attempts, remaining);
                  break;
          }
          showDialog(null, message);
      }
  
      private void showWipeDialog(int attempts, int userType) {
          String message = null;
          switch (userType) {
              case USER_TYPE_PRIMARY:
                  message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
                          attempts);
                  break;
              case USER_TYPE_SECONDARY_USER:
                  message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
                          attempts);
                  break;
              case USER_TYPE_WORK_PROFILE:
                  message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
                          attempts);
                  break;
          }
          showDialog(null, message);
      }
  
      private void showAlmostAtAccountLoginDialog() {
          final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
          final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
                  - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
          String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
                  count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
          showDialog(null, message);
      }
  
      private void reportFailedUnlockAttempt() {
          final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
          final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
  
          if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
  
          SecurityMode mode = mSecurityModel.getSecurityMode();
          final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
          final int currentUser = mLockPatternUtils.getCurrentUser();
          final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
          final int failedAttemptsBeforeWipe =
                  dpm.getMaximumFailedPasswordsForWipe(null, currentUser);
  
          final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
                  - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
  
          final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
                  (failedAttemptsBeforeWipe - failedAttempts)
                  : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
          boolean showTimeout = false;
          if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
              // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
              // N attempts. Once we get below the grace period, we post this dialog every time as a
              // clear warning until the deletion fires.
              // Check which profile has the strictest policy for failed password attempts
              final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser);
              int userType = USER_TYPE_PRIMARY;
              if (expiringUser == currentUser) {
                  if (expiringUser != UserHandle.USER_OWNER) {
                      userType = USER_TYPE_SECONDARY_USER;
                  }
              } else if (expiringUser != UserHandle.USER_NULL) {
                  userType = USER_TYPE_WORK_PROFILE;
              } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
              if (remainingBeforeWipe > 0) {
                  showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
              } else {
                  // Too many attempts. The device will be wiped shortly.
                  Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
                  showWipeDialog(failedAttempts, userType);
              }
          } else {
              showTimeout =
                  (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
              if (usingPattern && mEnableFallback) {
                  if (failedAttempts == failedAttemptWarning) {
                      showAlmostAtAccountLoginDialog();
                      showTimeout = false; // don't show both dialogs
                  } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
                      mLockPatternUtils.setPermanentlyLocked(true);
                      showSecurityScreen(SecurityMode.Account);
                      // don't show timeout dialog because we show account unlock screen next
                      showTimeout = false;
                  }
              }
          }
          monitor.reportFailedUnlockAttempt();
          mLockPatternUtils.reportFailedPasswordAttempt();
          if (showTimeout) {
              showTimeoutDialog();
          }
      }
  
      /**
       * Shows the primary security screen for the user. This will be either the multi-selector
       * or the user's security method.
       * @param turningOff true if the device is being turned off
       */
      void showPrimarySecurityScreen(boolean turningOff) {
          SecurityMode securityMode = mSecurityModel.getSecurityMode();
          if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
          if (!turningOff &&
                  KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
              // If we're not turning off, then allow biometric alternate.
              // We'll reload it when the device comes back on.
              securityMode = mSecurityModel.getAlternateFor(securityMode);
          }
          showSecurityScreen(securityMode);
      }
  
      /**
       * Shows the backup security screen for the current security mode.  This could be used for
       * password recovery screens but is currently only used for pattern unlock to show the
       * account unlock screen and biometric unlock to show the user's normal unlock.
       */
      private void showBackupSecurityScreen() {
          if (DEBUG) Log.d(TAG, "showBackupSecurity()");
          SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
          showSecurityScreen(backup);
      }
  
      /**
       * Shows the next security screen if there is one.
       * @param authenticated true if the user entered the correct authentication
       * @param authenticated
       * @return true if keyguard is done
       */
      boolean showNextSecurityScreenOrFinish(boolean authenticated) {
          if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
          boolean finish = false;
          if (mUpdateMonitor.getUserHasTrust(mLockPatternUtils.getCurrentUser())) {
              finish = true;
          } else if (SecurityMode.None == mCurrentSecuritySelection) {
              SecurityMode securityMode = mSecurityModel.getSecurityMode();
              // Allow an alternate, such as biometric unlock
              securityMode = mSecurityModel.getAlternateFor(securityMode);
              if (SecurityMode.None == securityMode) {
                  finish = true; // no security required
              } else {
                  showSecurityScreen(securityMode); // switch to the alternate security view
              }
          } else if (authenticated) {
              switch (mCurrentSecuritySelection) {
                  case Pattern:
                  case Password:
                  case PIN:
                  case Account:
                  case Biometric:
                      finish = true;
                      break;
  
                  case SimPin:
                  case SimPuk:
                      // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                      SecurityMode securityMode = mSecurityModel.getSecurityMode();
                      if (securityMode != SecurityMode.None) {
                          showSecurityScreen(securityMode);
                      } else {
                          finish = true;
                      }
                      break;
  
                  default:
                      Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
                      showPrimarySecurityScreen(false);
                      break;
              }
          }
          if (finish) {
              mSecurityCallback.finish();
          }
          return finish;
      }
  
      /**
       * Switches to the given security view unless it's already being shown, in which case
       * this is a no-op.
       *
       * @param securityMode
       */
      private void showSecurityScreen(SecurityMode securityMode) {
          if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
  
          if (securityMode == mCurrentSecuritySelection) return;
  
          KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
          KeyguardSecurityView newView = getSecurityView(securityMode);
  
          // Emulate Activity life cycle
          if (oldView != null) {
              oldView.onPause();
              oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
          }
          if (securityMode != SecurityMode.None) {
              newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
              newView.setKeyguardCallback(mCallback);
          }
  
          // Find and show this child.
          final int childCount = mSecurityViewFlipper.getChildCount();
  
          final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
          for (int i = 0; i < childCount; i++) {
              if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
                  mSecurityViewFlipper.setDisplayedChild(i);
                  break;
              }
          }
  
          mCurrentSecuritySelection = securityMode;
          mSecurityCallback.onSecurityModeChanged(securityMode,
                  securityMode != SecurityMode.None && newView.needsInput());
      }
  
      private KeyguardSecurityViewFlipper getFlipper() {
          for (int i = 0; i < getChildCount(); i++) {
              View child = getChildAt(i);
              if (child instanceof KeyguardSecurityViewFlipper) {
                  return (KeyguardSecurityViewFlipper) child;
              }
          }
          return null;
      }
  
      public void showBouncer(int duration) {
          KeyguardSecurityViewFlipper flipper = getFlipper();
  