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 static androidx.constraintlayout.widget.ConstraintSet.BOTTOM; 19 import static androidx.constraintlayout.widget.ConstraintSet.END; 20 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; 21 import static androidx.constraintlayout.widget.ConstraintSet.START; 22 import static androidx.constraintlayout.widget.ConstraintSet.TOP; 23 24 import android.annotation.Nullable; 25 import android.app.admin.IKeyguardCallback; 26 import android.app.admin.IKeyguardClient; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.ServiceConnection; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.util.Log; 36 import android.view.SurfaceControlViewHost; 37 import android.view.SurfaceHolder; 38 import android.view.SurfaceView; 39 import android.view.View; 40 41 import androidx.constraintlayout.widget.ConstraintLayout; 42 import androidx.constraintlayout.widget.ConstraintSet; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.keyguard.KeyguardSecurityModel.SecurityMode; 46 import com.android.keyguard.dagger.KeyguardBouncerScope; 47 import com.android.systemui.dagger.qualifiers.Main; 48 49 import java.util.NoSuchElementException; 50 51 import javax.inject.Inject; 52 53 /** 54 * Encapsulates all logic for secondary lockscreen state management. 55 */ 56 public class AdminSecondaryLockScreenController { 57 private static final String TAG = "AdminSecondaryLockScreenController"; 58 private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500; 59 private final KeyguardUpdateMonitor mUpdateMonitor; 60 private final Context mContext; 61 private final ConstraintLayout mParent; 62 private AdminSecurityView mView; 63 private Handler mHandler; 64 private IKeyguardClient mClient; 65 private KeyguardSecurityCallback mKeyguardCallback; 66 67 private final ServiceConnection mConnection = new ServiceConnection() { 68 @Override 69 public void onServiceConnected(ComponentName className, IBinder service) { 70 mClient = IKeyguardClient.Stub.asInterface(service); 71 if (mView.isAttachedToWindow() && mClient != null) { 72 onSurfaceReady(); 73 74 try { 75 service.linkToDeath(mKeyguardClientDeathRecipient, 0); 76 } catch (RemoteException e) { 77 // Failed to link to death, just dismiss and unbind the service for now. 78 Log.e(TAG, "Lost connection to secondary lockscreen service", e); 79 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 80 } 81 } 82 } 83 84 @Override 85 public void onServiceDisconnected(ComponentName className) { 86 mClient = null; 87 } 88 }; 89 90 private final IBinder.DeathRecipient mKeyguardClientDeathRecipient = () -> { 91 hide(); // hide also takes care of unlinking to death. 92 Log.d(TAG, "KeyguardClient service died"); 93 }; 94 95 private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() { 96 @Override 97 public void onDismiss() { 98 mHandler.post(() -> { 99 dismiss(UserHandle.getCallingUserId()); 100 }); 101 } 102 103 @Override 104 public void onRemoteContentReady( 105 @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) { 106 if (mHandler != null) { 107 mHandler.removeCallbacksAndMessages(null); 108 } 109 if (surfacePackage != null) { 110 mView.setChildSurfacePackage(surfacePackage); 111 } else { 112 mHandler.post(() -> { 113 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 114 }); 115 } 116 } 117 }; 118 119 private final KeyguardUpdateMonitorCallback mUpdateCallback = 120 new KeyguardUpdateMonitorCallback() { 121 @Override 122 public void onSecondaryLockscreenRequirementChanged(int userId) { 123 Intent newIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(userId); 124 if (newIntent == null) { 125 dismiss(userId); 126 } 127 } 128 }; 129 130 @VisibleForTesting 131 protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { 132 @Override 133 public void surfaceCreated(SurfaceHolder holder) { 134 final int userId = KeyguardUpdateMonitor.getCurrentUser(); 135 mUpdateMonitor.registerCallback(mUpdateCallback); 136 137 if (mClient != null) { 138 onSurfaceReady(); 139 } 140 mHandler.postDelayed( 141 () -> { 142 // If the remote content is not readied within the timeout period, 143 // move on without the secondary lockscreen. 144 dismiss(userId); 145 Log.w(TAG, "Timed out waiting for secondary lockscreen content."); 146 }, 147 REMOTE_CONTENT_READY_TIMEOUT_MILLIS); 148 } 149 150 @Override 151 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} 152 153 @Override 154 public void surfaceDestroyed(SurfaceHolder holder) { 155 mUpdateMonitor.removeCallback(mUpdateCallback); 156 } 157 }; 158 AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent, KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, @Main Handler handler)159 private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent, 160 KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, 161 @Main Handler handler) { 162 mContext = context; 163 mHandler = handler; 164 mParent = parent; 165 mUpdateMonitor = updateMonitor; 166 mKeyguardCallback = callback; 167 mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); 168 mView.setId(View.generateViewId()); 169 } 170 171 /** 172 * Displays the Admin security Surface view. 173 */ show(Intent serviceIntent)174 public void show(Intent serviceIntent) { 175 if (mClient == null) { 176 mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); 177 } 178 if (!mView.isAttachedToWindow()) { 179 mParent.addView(mView); 180 ConstraintSet constraintSet = new ConstraintSet(); 181 constraintSet.clone(mParent); 182 constraintSet.connect(mView.getId(), TOP, PARENT_ID, TOP); 183 constraintSet.connect(mView.getId(), START, PARENT_ID, START); 184 constraintSet.connect(mView.getId(), END, PARENT_ID, END); 185 constraintSet.connect(mView.getId(), BOTTOM, PARENT_ID, BOTTOM); 186 constraintSet.constrainHeight(mView.getId(), ConstraintSet.MATCH_CONSTRAINT); 187 constraintSet.constrainWidth(mView.getId(), ConstraintSet.MATCH_CONSTRAINT); 188 constraintSet.applyTo(mParent); 189 } 190 } 191 192 /** 193 * Hides the Admin security Surface view. 194 */ hide()195 public void hide() { 196 if (mView.isAttachedToWindow()) { 197 mParent.removeView(mView); 198 } 199 if (mClient != null) { 200 try { 201 mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0); 202 } catch (NoSuchElementException e) { 203 Log.w(TAG, "IKeyguardClient death recipient already released"); 204 } 205 mContext.unbindService(mConnection); 206 mClient = null; 207 } 208 } 209 onSurfaceReady()210 private void onSurfaceReady() { 211 try { 212 IBinder hostToken = mView.getHostToken(); 213 // Should never be null when SurfaceView is attached to window. 214 if (hostToken != null) { 215 mClient.onCreateKeyguardSurface(hostToken, mCallback); 216 } else { 217 hide(); 218 } 219 } catch (RemoteException e) { 220 Log.e(TAG, "Error in onCreateKeyguardSurface", e); 221 dismiss(KeyguardUpdateMonitor.getCurrentUser()); 222 } 223 } 224 dismiss(int userId)225 private void dismiss(int userId) { 226 mHandler.removeCallbacksAndMessages(null); 227 if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) { 228 hide(); 229 if (mKeyguardCallback != null) { 230 mKeyguardCallback.dismiss(/* securityVerified= */ true, userId, 231 /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid); 232 } 233 } 234 } 235 236 /** 237 * Custom {@link SurfaceView} used to allow a device admin to present an additional security 238 * screen. 239 */ 240 private class AdminSecurityView extends SurfaceView { 241 private SurfaceHolder.Callback mSurfaceHolderCallback; 242 AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback)243 AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback) { 244 super(context); 245 mSurfaceHolderCallback = surfaceHolderCallback; 246 setZOrderOnTop(true); 247 } 248 249 @Override onAttachedToWindow()250 protected void onAttachedToWindow() { 251 super.onAttachedToWindow(); 252 getHolder().addCallback(mSurfaceHolderCallback); 253 } 254 255 @Override onDetachedFromWindow()256 protected void onDetachedFromWindow() { 257 super.onDetachedFromWindow(); 258 getHolder().removeCallback(mSurfaceHolderCallback); 259 } 260 } 261 262 @KeyguardBouncerScope 263 public static class Factory { 264 private final Context mContext; 265 private final KeyguardSecurityContainer mParent; 266 private final KeyguardUpdateMonitor mUpdateMonitor; 267 private final Handler mHandler; 268 269 @Inject Factory(Context context, KeyguardSecurityContainer parent, KeyguardUpdateMonitor updateMonitor, @Main Handler handler)270 public Factory(Context context, KeyguardSecurityContainer parent, 271 KeyguardUpdateMonitor updateMonitor, @Main Handler handler) { 272 mContext = context; 273 mParent = parent; 274 mUpdateMonitor = updateMonitor; 275 mHandler = handler; 276 } 277 create(KeyguardSecurityCallback callback)278 public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) { 279 return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor, 280 callback, mHandler); 281 } 282 } 283 } 284