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