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