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