content: package com.android.server.policy.keyguard;
  
  import static com.android.server.wm.KeyguardServiceDelegateProto.INTERACTIVE_STATE;
  import static com.android.server.wm.KeyguardServiceDelegateProto.OCCLUDED;
  import static com.android.server.wm.KeyguardServiceDelegateProto.SCREEN_STATE;
  import static com.android.server.wm.KeyguardServiceDelegateProto.SECURE;
  import static com.android.server.wm.KeyguardServiceDelegateProto.SHOWING;
  
  import android.app.ActivityTaskManager;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.content.res.Resources;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.os.UserHandle;
  import android.util.Log;
  import android.util.Slog;
  import android.util.proto.ProtoOutputStream;
  import android.view.WindowManagerPolicyConstants;
  
  import com.android.internal.policy.IKeyguardDismissCallback;
  import com.android.internal.policy.IKeyguardDrawnCallback;
  import com.android.internal.policy.IKeyguardExitCallback;
  import com.android.internal.policy.IKeyguardService;
  import com.android.server.UiThread;
  import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
  
  import java.io.PrintWriter;
  
  /**
   * A local class that keeps a cache of keyguard state that can be restored in the event
   * keyguard crashes. It currently also allows runtime-selectable
   * local or remote instances of keyguard.
   */
  public class KeyguardServiceDelegate {
      private static final String TAG = "KeyguardServiceDelegate";
      private static final boolean DEBUG = false;
  
      private static final int SCREEN_STATE_OFF = 0;
      private static final int SCREEN_STATE_TURNING_ON = 1;
      private static final int SCREEN_STATE_ON = 2;
      private static final int SCREEN_STATE_TURNING_OFF = 3;
  
      private static final int INTERACTIVE_STATE_SLEEP = 0;
      private static final int INTERACTIVE_STATE_WAKING = 1;
      private static final int INTERACTIVE_STATE_AWAKE = 2;
      private static final int INTERACTIVE_STATE_GOING_TO_SLEEP = 3;
  
      protected KeyguardServiceWrapper mKeyguardService;
      private final Context mContext;
      private final Handler mHandler;
      private final KeyguardState mKeyguardState = new KeyguardState();
      private final KeyguardStateMonitor.StateCallback mCallback;
  
      private DrawnListener mDrawnListenerWhenConnect;
  
      private static final class KeyguardState {
          KeyguardState() {
              reset();
          }
          boolean showing;
          boolean showingAndNotOccluded;
          boolean inputRestricted;
          boolean occluded;
          boolean secure;
          boolean dreaming;
          boolean systemIsReady;
          boolean deviceHasKeyguard;
          public boolean enabled;
          public int offReason;
          public int currentUser;
          public boolean bootCompleted;
          public int screenState;
          public int interactiveState;
  
          private void reset() {
              // Assume keyguard is showing and secure until we know for sure. This is here in
              // the event something checks before the service is actually started.
              // KeyguardService itself should default to this state until the real state is known.
              showing = true;
              showingAndNotOccluded = true;
              secure = true;
              deviceHasKeyguard = true;
              enabled = true;
              currentUser = UserHandle.USER_NULL;
          }
      };
  
      public interface DrawnListener {
          void onDrawn();
      }
  
      // A delegate class to map a particular invocation with a ShowListener object.
      private final class KeyguardShowDelegate extends IKeyguardDrawnCallback.Stub {
          private DrawnListener mDrawnListener;
  
          KeyguardShowDelegate(DrawnListener drawnListener) {
              mDrawnListener = drawnListener;
          }
  
          @Override
          public void onDrawn() throws RemoteException {
              if (DEBUG) Log.v(TAG, "**** SHOWN CALLED ****");
              if (mDrawnListener != null) {
                  mDrawnListener.onDrawn();
              }
          }
      };
  
      // A delegate class to map a particular invocation with an OnKeyguardExitResult object.
      private final class KeyguardExitDelegate extends IKeyguardExitCallback.Stub {
          private OnKeyguardExitResult mOnKeyguardExitResult;
  
          KeyguardExitDelegate(OnKeyguardExitResult onKeyguardExitResult) {
              mOnKeyguardExitResult = onKeyguardExitResult;
          }
  
          @Override
          public void onKeyguardExitResult(boolean success) throws RemoteException {
              if (DEBUG) Log.v(TAG, "**** onKeyguardExitResult(" + success +") CALLED ****");
              if (mOnKeyguardExitResult != null) {
                  mOnKeyguardExitResult.onKeyguardExitResult(success);
              }
          }
      };
  
      public KeyguardServiceDelegate(Context context, KeyguardStateMonitor.StateCallback callback) {
          mContext = context;
          mHandler = UiThread.getHandler();
          mCallback = callback;
      }
  
      public void bindService(Context context) {
          Intent intent = new Intent();
          final Resources resources = context.getApplicationContext().getResources();
  
          final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                  resources.getString(com.android.internal.R.string.config_keyguardComponent));
          intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
          intent.setComponent(keyguardComponent);
  
          if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                  Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
              Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
              mKeyguardState.showing = false;
              mKeyguardState.showingAndNotOccluded = false;
              mKeyguardState.secure = false;
              synchronized (mKeyguardState) {
                  // TODO: Fix synchronisation model in this class. The other state in this class
                  // is at least self-healing but a race condition here can lead to the scrim being
                  // stuck on keyguard-less devices.
                  mKeyguardState.deviceHasKeyguard = false;
              }
          } else {
              if (DEBUG) Log.v(TAG, "*** Keyguard started");
          }
      }
  
      private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
              mKeyguardService = new KeyguardServiceWrapper(mContext,
                      IKeyguardService.Stub.asInterface(service), mCallback);
              if (mKeyguardState.systemIsReady) {
                  // If the system is ready, it means keyguard crashed and restarted.
                  mKeyguardService.onSystemReady();
                  if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
                      // There has been a user switch earlier
                      mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
                  }
                  // This is used to hide the scrim once keyguard displays.
                  if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
                          || mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
                      mKeyguardService.onStartedWakingUp();
                  }
                  if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                      mKeyguardService.onFinishedWakingUp();
                  }
                  if (mKeyguardState.screenState == SCREEN_STATE_ON
                          || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
                      mKeyguardService.onScreenTurningOn(
                              new KeyguardShowDelegate(mDrawnListenerWhenConnect));
                  }
                  if (mKeyguardState.screenState == SCREEN_STATE_ON) {
                      mKeyguardService.onScreenTurnedOn();
                  }
                  mDrawnListenerWhenConnect = null;
              }
              if (mKeyguardState.bootCompleted) {
                  mKeyguardService.onBootCompleted();
              }
              if (mKeyguardState.occluded) {
                  mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);
              }
              if (!mKeyguardState.enabled) {
                  mKeyguardService.setKeyguardEnabled(mKeyguardState.enabled);
              }
          }
  
          @Override
          public void onServiceDisconnected(ComponentName name) {
              if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
              mKeyguardService = null;
              mKeyguardState.reset();
              mHandler.post(() -> {
                  try {
                      ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */,
                              false /* aodShowing */);
                  } catch (RemoteException e) {
                      // Local call.
                  }
              });
          }
      };
  
      public boolean isShowing() {
          if (mKeyguardService != null) {
              mKeyguardState.showing = mKeyguardService.isShowing();
          }
          return mKeyguardState.showing;
      }
  
      public boolean isTrusted() {
          if (mKeyguardService != null) {
              return mKeyguardService.isTrusted();
          }
          return false;
      }
  
      public boolean hasLockscreenWallpaper() {
          if (mKeyguardService != null) {
              return mKeyguardService.hasLockscreenWallpaper();
          }
          return false;
      }
  
      public boolean hasKeyguard() {
          return mKeyguardState.deviceHasKeyguard;
      }
  
      public boolean isInputRestricted() {
          if (mKeyguardService != null) {
              mKeyguardState.inputRestricted = mKeyguardService.isInputRestricted();
          }
          return mKeyguardState.inputRestricted;
      }
  
      public void verifyUnlock(final OnKeyguardExitResult onKeyguardExitResult) {
          if (mKeyguardService != null) {
              mKeyguardService.verifyUnlock(new KeyguardExitDelegate(onKeyguardExitResult));
          }
      }
  
      public void setOccluded(boolean isOccluded, boolean animate) {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
              mKeyguardService.setOccluded(isOccluded, animate);
          }
          mKeyguardState.occluded = isOccluded;
      }
  
      public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
          if (mKeyguardService != null) {
              mKeyguardService.dismiss(callback, message);
          }
      }
  
      public boolean isSecure(int userId) {
          if (mKeyguardService != null) {
              mKeyguardState.secure = mKeyguardService.isSecure(userId);
          }
          return mKeyguardState.secure;
      }
  
      public void onDreamingStarted() {
          if (mKeyguardService != null) {
              mKeyguardService.onDreamingStarted();
          }
          mKeyguardState.dreaming = true;
      }
  
      public void onDreamingStopped() {
          if (mKeyguardService != null) {
              mKeyguardService.onDreamingStopped();
          }
          mKeyguardState.dreaming = false;
      }
  
      public void onStartedWakingUp() {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
              mKeyguardService.onStartedWakingUp();
          }
          mKeyguardState.interactiveState = INTERACTIVE_STATE_WAKING;
      }
  
      public void onFinishedWakingUp() {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onFinishedWakingUp()");
              mKeyguardService.onFinishedWakingUp();
          }
          mKeyguardState.interactiveState = INTERACTIVE_STATE_AWAKE;
      }
  
      public void onScreenTurningOff() {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onScreenTurningOff()");
              mKeyguardService.onScreenTurningOff();
          }
          mKeyguardState.screenState = SCREEN_STATE_TURNING_OFF;
      }
  
      public void onScreenTurnedOff() {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onScreenTurnedOff()");
              mKeyguardService.onScreenTurnedOff();
          }
          mKeyguardState.screenState = SCREEN_STATE_OFF;
      }
  
      public void onScreenTurningOn(final DrawnListener drawnListener) {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onScreenTurnedOn(showListener = " + drawnListener + ")");
              mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(drawnListener));
          } else {
              // try again when we establish a connection
              Slog.w(TAG, "onScreenTurningOn(): no keyguard service!");
              // This shouldn't happen, but if it does, show the scrim immediately and
              // invoke the listener's callback after the service actually connects.
              mDrawnListenerWhenConnect = drawnListener;
          }
          mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
      }
  
      public void onScreenTurnedOn() {
          if (mKeyguardService != null) {
              if (DEBUG) Log.v(TAG, "onScreenTurnedOn()");
              mKeyguardService.onScreenTurnedOn();
          }
          mKeyguardState.screenState = SCREEN_STATE_ON;
      }
  
      public void onStartedGoingToSleep(int why) {
          if (mKeyguardService != null) {
              mKeyguardService.onStartedGoingToSleep(why);
          }
          mKeyguardState.offReason = why;
          mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
      }
  
      public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
          if (mKeyguardService != null) {
              mKeyguardService.onFinishedGoingToSleep(why, cameraGestureTriggered);
          }
          mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
      }
  
      public void setKeyguardEnabled(boolean enabled) {
          if (mKeyguardService != null) {
              mKeyguardService.setKeyguardEnabled(enabled);
          }
          mKeyguardState.enabled = enabled;
      }
  
      public void onSystemReady() {
          if (mKeyguardService != null) {
              mKeyguardService.onSystemReady();
          } else {
              mKeyguardState.systemIsReady = true;
          }
      }
  
      public void doKeyguardTimeout(Bundle options) {
          if (mKeyguardService != null) {
              mKeyguardService.doKeyguardTimeout(options);
          }
      }
  
      public void setCurrentUser(int newUserId) {
          if (mKeyguardService != null) {
              mKeyguardService.setCurrentUser(newUserId);
          }
          mKeyguardState.currentUser = newUserId;
      }
  
      public void setSwitchingUser(boolean switching) {
          if (mKeyguardService != null) {
              mKeyguardService.setSwitchingUser(switching);
          }
      }
  
      public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
          if (mKeyguardService != null) {
              mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
          }
      }
  
      public void onBootCompleted() {
          if (mKeyguardService != null) {
              mKeyguardService.onBootCompleted();
          }
          mKeyguardState.bootCompleted = true;
      }
  
      public void onShortPowerPressedGoHome() {
          if (mKeyguardService != null) {
              mKeyguardService.onShortPowerPressedGoHome();
          }
      }
  
      public void writeToProto(ProtoOutputStream proto, long fieldId) {
          final long token = proto.start(fieldId);
          proto.write(SHOWING, mKeyguardState.showing);
          proto.write(OCCLUDED, mKeyguardState.occluded);
          proto.write(SECURE, mKeyguardState.secure);
          proto.write(SCREEN_STATE, mKeyguardState.screenState);
          proto.write(INTERACTIVE_STATE, mKeyguardState.interactiveState);
          proto.end(token);
      }
  
      public void dump(String prefix, PrintWriter pw) {
          pw.println(prefix + TAG);
          prefix += "  ";
          pw.println(prefix + "showing=" + mKeyguardState.showing);
          pw.println(prefix + "showingAndNotOccluded=" + mKeyguardState.showingAndNotOccluded);
          pw.println(prefix + "inputRestricted=" + mKeyguardState.inputRestricted);
          pw.println(prefix + "occluded=" + mKeyguardState.occluded);
          pw.println(prefix + "secure=" + mKeyguardState.secure);
          pw.println(prefix + "dreaming=" + mKeyguardState.dreaming);
          pw.println(prefix + "systemIsReady=" + mKeyguardState.systemIsReady);
          pw.println(prefix + "deviceHasKeyguard=" + mKeyguardState.deviceHasKeyguard);
          pw.println(prefix + "enabled=" + mKeyguardState.enabled);
          pw.println(prefix + "offReason=" +
                  WindowManagerPolicyConstants.offReasonToString(mKeyguardState.offReason));
          pw.println(prefix + "currentUser=" + mKeyguardState.currentUser);
          pw.println(prefix + "bootCompleted=" + mKeyguardState.bootCompleted);
          pw.println(prefix + "screenState=" + screenStateToString(mKeyguardState.screenState));
          pw.println(prefix + "interactiveState=" +
                  interactiveStateToString(mKeyguardState.interactiveState));
          if (mKeyguardService != null) {
              mKeyguardService.dump(prefix, pw);
          }
      }
  
      private static String screenStateToString(int screen) {
          switch (screen) {
              case SCREEN_STATE_OFF:
                  return "SCREEN_STATE_OFF";
              case SCREEN_STATE_TURNING_ON:
                  return "SCREEN_STATE_TURNING_ON";
              case SCREEN_STATE_ON:
                  return "SCREEN_STATE_ON";
              case SCREEN_STATE_TURNING_OFF:
                  return "SCREEN_STATE_TURNING_OFF";
              default:
                  return Integer.toString(screen);
          }
      }
  
      private static String interactiveStateToString(int interactive) {
          switch (interactive) {
              case INTERACTIVE_STATE_SLEEP:
                  return "INTERACTIVE_STATE_SLEEP";
              case INTERACTIVE_STATE_WAKING:
                  return "INTERACTIVE_STATE_WAKING";
              case INTERACTIVE_STATE_AWAKE:
                  return "INTERACTIVE_STATE_AWAKE";
              case INTERACTIVE_STATE_GOING_TO_SLEEP:
                  return "INTERACTIVE_STATE_GOING_TO_SLEEP";
              default:
                  return Integer.toString(interactive);
          }
      }
  }
  