summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java')
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java216
1 files changed, 216 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
new file mode 100644
index 000000000000..2f8ef2dc8828
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2020 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.annotation.Nullable;
+import android.app.admin.IKeyguardCallback;
+import android.app.admin.IKeyguardClient;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Encapsulates all logic for secondary lockscreen state management.
+ */
+public class AdminSecondaryLockScreenController {
+ private static final String TAG = "AdminSecondaryLockScreenController";
+ private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final Context mContext;
+ private final ViewGroup mParent;
+ private AdminSecurityView mView;
+ private Handler mHandler;
+ private IKeyguardClient mClient;
+ private KeyguardSecurityCallback mKeyguardCallback;
+ private SurfaceControl.Transaction mTransaction;
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mClient = IKeyguardClient.Stub.asInterface(service);
+ if (mView.isAttachedToWindow() && mClient != null) {
+ onSurfaceReady();
+
+ try {
+ service.linkToDeath(mKeyguardClientDeathRecipient, 0);
+ } catch (RemoteException e) {
+ // Failed to link to death, just dismiss and unbind the service for now.
+ Log.e(TAG, "Lost connection to secondary lockscreen service", e);
+ dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mClient = null;
+ }
+ };
+
+ private final IBinder.DeathRecipient mKeyguardClientDeathRecipient = () -> {
+ hide(); // hide also takes care of unlinking to death.
+ Log.d(TAG, "KeyguardClient service died");
+ };
+
+ private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() {
+ @Override
+ public void onDismiss() {
+ dismiss(UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) {
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+ if (remoteSurfaceControl != null) {
+ mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl())
+ .apply();
+ } else {
+ dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ }
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSecondaryLockscreenRequirementChanged(int userId) {
+ Intent newIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(userId);
+ if (newIntent == null) {
+ dismiss(userId);
+ }
+ }
+ };
+
+ @VisibleForTesting
+ protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ mUpdateMonitor.registerCallback(mUpdateCallback);
+
+ if (mClient != null) {
+ onSurfaceReady();
+ }
+ mHandler.postDelayed(
+ () -> {
+ // If the remote content is not readied within the timeout period,
+ // move on without the secondary lockscreen.
+ dismiss(userId);
+ },
+ REMOTE_CONTENT_READY_TIMEOUT_MILLIS);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mUpdateMonitor.removeCallback(mUpdateCallback);
+ }
+ };
+
+ public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
+ KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
+ Handler handler, SurfaceControl.Transaction transaction) {
+ mContext = context;
+ mHandler = handler;
+ mParent = parent;
+ mTransaction = transaction;
+ mUpdateMonitor = updateMonitor;
+ mKeyguardCallback = callback;
+ mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
+ }
+
+ /**
+ * Displays the Admin security Surface view.
+ */
+ public void show(Intent serviceIntent) {
+ mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
+ mParent.addView(mView);
+ }
+
+ /**
+ * Hides the Admin security Surface view.
+ */
+ public void hide() {
+ if (mView.isAttachedToWindow()) {
+ mParent.removeView(mView);
+ }
+ if (mClient != null) {
+ mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+ mContext.unbindService(mConnection);
+ mClient = null;
+ }
+ }
+
+ private void onSurfaceReady() {
+ try {
+ mClient.onSurfaceReady(mView.getInputToken(), mCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in onSurfaceReady", e);
+ dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ }
+ }
+
+ private void dismiss(int userId) {
+ mHandler.removeCallbacksAndMessages(null);
+ if (mView != null && mView.isAttachedToWindow()
+ && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ hide();
+ mKeyguardCallback.dismiss(true, userId);
+ }
+ }
+
+ /**
+ * Custom {@link SurfaceView} used to allow a device admin to present an additional security
+ * screen.
+ */
+ private class AdminSecurityView extends SurfaceView {
+ private SurfaceHolder.Callback mSurfaceHolderCallback;
+
+ AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback) {
+ super(context);
+ mSurfaceHolderCallback = surfaceHolderCallback;
+ setZOrderOnTop(true);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getHolder().addCallback(mSurfaceHolderCallback);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getHolder().removeCallback(mSurfaceHolderCallback);
+ }
+ }
+}