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