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