1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.keyguard; 17 18 import android.annotation.Nullable; 19 import android.app.admin.IKeyguardCallback; 20 import android.app.admin.IKeyguardClient; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.os.UserHandle; 29 import android.util.Log; 30 import android.view.SurfaceControlViewHost; 31 import android.view.SurfaceHolder; 32 import android.view.SurfaceView; 33 import android.view.ViewGroup; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.util.NoSuchElementException; 38 39 /** 40 * Encapsulates all logic for secondary lockscreen state management. 41 */ 42 public class AdminSecondaryLockScreenController { 43 private static final String TAG = "AdminSecondaryLockScreenController"; 44 private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500; 45 private final KeyguardUpdateMonitor mUpdateMonitor; 46 private final Context mContext; 47 private final ViewGroup mParent; 48 private AdminSecurityView mView; 49 private Handler mHandler; 50 private IKeyguardClient mClient; 51 private KeyguardSecurityCallback mKeyguardCallback; 52 53 private final ServiceConnection mConnection = new ServiceConnection() { 54 @Override 55 public void onServiceConnected(ComponentName className, IBinder service) { 56 mClient = IKeyguardClient.Stub.asInterface(service); 57 if (mView.isAttachedToWindow() && mClient != null) { 58 onSurfaceReady(); 59 60 try { 61 service.linkToDeath(mKeyguardClientDeathRecipient, 0); 62 } catch (RemoteException e) { 63 // Failed to link to death, just dismiss and unbind the service for now. 64 Log.e(TAG, "Lost connection to secondary lockscreen service", e); 65 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 66 } 67 } 68 } 69 70 @Override 71 public void onServiceDisconnected(ComponentName className) { 72 mClient = null; 73 } 74 }; 75 76 private final IBinder.DeathRecipient mKeyguardClientDeathRecipient = () -> { 77 hide(); // hide also takes care of unlinking to death. 78 Log.d(TAG, "KeyguardClient service died"); 79 }; 80 81 private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() { 82 @Override 83 public void onDismiss() { 84 mHandler.post(() -> { 85 dismiss(UserHandle.getCallingUserId()); 86 }); 87 } 88 89 @Override 90 public void onRemoteContentReady( 91 @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) { 92 if (mHandler != null) { 93 mHandler.removeCallbacksAndMessages(null); 94 } 95 if (surfacePackage != null) { 96 mView.setChildSurfacePackage(surfacePackage); 97 } else { 98 mHandler.post(() -> { 99 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 100 }); 101 } 102 } 103 }; 104 105 private final KeyguardUpdateMonitorCallback mUpdateCallback = 106 new KeyguardUpdateMonitorCallback() { 107 @Override 108 public void onSecondaryLockscreenRequirementChanged(int userId) { 109 Intent newIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(userId); 110 if (newIntent == null) { 111 dismiss(userId); 112 } 113 } 114 }; 115 116 @VisibleForTesting 117 protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { 118 @Override 119 public void surfaceCreated(SurfaceHolder holder) { 120 final int userId = KeyguardUpdateMonitor.getCurrentUser(); 121 mUpdateMonitor.registerCallback(mUpdateCallback); 122 123 if (mClient != null) { 124 onSurfaceReady(); 125 } 126 mHandler.postDelayed( 127 () -> { 128 // If the remote content is not readied within the timeout period, 129 // move on without the secondary lockscreen. 130 dismiss(userId); 131 Log.w(TAG, "Timed out waiting for secondary lockscreen content."); 132 }, 133 REMOTE_CONTENT_READY_TIMEOUT_MILLIS); 134 } 135 136 @Override 137 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} 138 139 @Override 140 public void surfaceDestroyed(SurfaceHolder holder) { 141 mUpdateMonitor.removeCallback(mUpdateCallback); 142 } 143 }; 144 AdminSecondaryLockScreenController(Context context, ViewGroup parent, KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, Handler handler)145 public AdminSecondaryLockScreenController(Context context, ViewGroup parent, 146 KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, 147 Handler handler) { 148 mContext = context; 149 mHandler = handler; 150 mParent = parent; 151 mUpdateMonitor = updateMonitor; 152 mKeyguardCallback = callback; 153 mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); 154 } 155 156 /** 157 * Displays the Admin security Surface view. 158 */ show(Intent serviceIntent)159 public void show(Intent serviceIntent) { 160 if (mClient == null) { 161 mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); 162 } 163 if (!mView.isAttachedToWindow()) { 164 mParent.addView(mView); 165 } 166 } 167 168 /** 169 * Hides the Admin security Surface view. 170 */ hide()171 public void hide() { 172 if (mView.isAttachedToWindow()) { 173 mParent.removeView(mView); 174 } 175 if (mClient != null) { 176 try { 177 mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0); 178 } catch (NoSuchElementException e) { 179 Log.w(TAG, "IKeyguardClient death recipient already released"); 180 } 181 mContext.unbindService(mConnection); 182 mClient = null; 183 } 184 } 185 onSurfaceReady()186 private void onSurfaceReady() { 187 try { 188 IBinder hostToken = mView.getHostToken(); 189 // Should never be null when SurfaceView is attached to window. 190 if (hostToken != null) { 191 mClient.onCreateKeyguardSurface(hostToken, mCallback); 192 } else { 193 hide(); 194 } 195 } catch (RemoteException e) { 196 Log.e(TAG, "Error in onCreateKeyguardSurface", e); 197 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 198 } 199 } 200 dismiss(int userId)201 private void dismiss(int userId) { 202 mHandler.removeCallbacksAndMessages(null); 203 if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) { 204 hide(); 205 if (mKeyguardCallback != null) { 206 mKeyguardCallback.dismiss(/* securityVerified= */ true, userId, 207 /* bypassSecondaryLockScreen= */true); 208 } 209 } 210 } 211 212 /** 213 * Custom {@link SurfaceView} used to allow a device admin to present an additional security 214 * screen. 215 */ 216 private class AdminSecurityView extends SurfaceView { 217 private SurfaceHolder.Callback mSurfaceHolderCallback; 218 AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback)219 AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback) { 220 super(context); 221 mSurfaceHolderCallback = surfaceHolderCallback; 222 setZOrderOnTop(true); 223 } 224 225 @Override onAttachedToWindow()226 protected void onAttachedToWindow() { 227 super.onAttachedToWindow(); 228 getHolder().addCallback(mSurfaceHolderCallback); 229 } 230 231 @Override onDetachedFromWindow()232 protected void onDetachedFromWindow() { 233 super.onDetachedFromWindow(); 234 getHolder().removeCallback(mSurfaceHolderCallback); 235 } 236 } 237 } 238