diff options
Diffstat (limited to 'packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java')
-rw-r--r-- | packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java | 216 |
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); + } + } +} |