blob: ec72ee74f59afcbe8fafc6a7596319c07bb778fb [file] [log] [blame]
Erin Yan8182bfd2019-08-14 12:03:12 -07001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.statusbar.car;
18
19import android.app.admin.DevicePolicyManager;
20import android.bluetooth.BluetoothAdapter;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.graphics.PixelFormat;
26import android.os.Handler;
27import android.os.UserHandle;
28import android.os.UserManager;
29import android.util.Log;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.WindowManager;
33import android.widget.Button;
34import android.widget.ImageView;
35import android.widget.TextView;
36
37import com.android.internal.widget.LockPatternUtils;
38import com.android.systemui.R;
39
40/**
41 * A helper class displays an unlock dialog and receives broadcast about detecting trusted device
42 * & unlocking state to show the appropriate message on the dialog.
43 */
44class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
45 private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName();
46
47 private final Context mContext;
48 private final WindowManager mWindowManager;
49 private final UserManager mUserManager;
50 private final WindowManager.LayoutParams mParams;
51 /**
52 * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an
53 * activity.
54 */
Erin Yan5eb60a22019-09-04 11:55:30 -070055 private final View mUnlockDialogLayout;
Erin Yan8182bfd2019-08-14 12:03:12 -070056 private final TextView mUnlockingText;
57 private final Button mButton;
58 private final IntentFilter mFilter;
59 private int mUid;
60 private boolean mIsDialogShowing;
61 private OnHideListener mOnHideListener;
62
63 CarTrustAgentUnlockDialogHelper(Context context) {
64 mContext = context;
65 mUserManager = mContext.getSystemService(UserManager.class);
66 mWindowManager = mContext.getSystemService(WindowManager.class);
67 mParams = createLayoutParams();
68 mFilter = getIntentFilter();
69
70 mParams.packageName = mContext.getPackageName();
71 mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));
72
Erin Yan5eb60a22019-09-04 11:55:30 -070073 mUnlockDialogLayout = LayoutInflater.from(mContext).inflate(
Erin Yan8182bfd2019-08-14 12:03:12 -070074 R.layout.trust_agent_unlock_dialog, null);
Erin Yan5eb60a22019-09-04 11:55:30 -070075 mUnlockDialogLayout.setLayoutParams(mParams);
Erin Yan8182bfd2019-08-14 12:03:12 -070076
Erin Yan5eb60a22019-09-04 11:55:30 -070077 View dialogParent = mUnlockDialogLayout.findViewById(R.id.unlock_dialog_parent);
78 dialogParent.setOnTouchListener((v, event)-> {
79 hideUnlockDialog(/* dismissUserSwitcher= */ false);
80 return true;
81 });
82 View unlockDialog = mUnlockDialogLayout.findViewById(R.id.unlock_dialog);
83 unlockDialog.setOnTouchListener((v, event) -> {
84 // If the person taps inside the unlock dialog, the touch event will be intercepted here
85 // and the dialog will not exit
86 return true;
87 });
88 mUnlockingText = mUnlockDialogLayout.findViewById(R.id.unlocking_text);
89 mButton = mUnlockDialogLayout.findViewById(R.id.enter_pin_button);
Erin Yan8182bfd2019-08-14 12:03:12 -070090 mButton.setOnClickListener(v -> {
Erin Yan5eb60a22019-09-04 11:55:30 -070091 hideUnlockDialog(/* dismissUserSwitcher= */true);
Erin Yan8182bfd2019-08-14 12:03:12 -070092 // TODO(b/138250105) Stop unlock advertising
93 });
94
95 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
96 if (bluetoothAdapter != null
97 && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) {
98 mUnlockingText.setText(R.string.unlock_dialog_message_start);
99 }
100 }
101
102 /**
103 * This filter is listening on:
104 * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising;
105 * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked
106 */
107 private IntentFilter getIntentFilter() {
108 IntentFilter filter = new IntentFilter();
109 filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
110 filter.addAction(Intent.ACTION_USER_UNLOCKED);
111 return filter;
112 }
113
114 /**
115 * Show dialog for the given user
116 */
117 void showUnlockDialog(int uid, OnHideListener listener) {
118 showUnlockDialogAfterDelay(uid, 0, listener);
119 }
120
121 /**
122 * Show dialog for the given user after the certain time of delay has elapsed
123 *
124 * @param uid the user to unlock
125 * @param listener listener that listens to dialog hide
126 */
127 void showUnlockDialogAfterDelay(int uid, OnHideListener listener) {
128 long delayMillis = mContext.getResources().getInteger(R.integer.unlock_dialog_delay_ms);
129 showUnlockDialogAfterDelay(uid, delayMillis, listener);
130 }
131
132 /**
133 * Show dialog for the given user after the supplied delay has elapsed
134 */
135 private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) {
136 setUid(uid);
137 mOnHideListener = listener;
138 if (!mIsDialogShowing) {
139 logd("Receiver registered");
140 mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter,
141 /* broadcastPermission= */ null,
142 /* scheduler= */ null);
143 new Handler().postDelayed(() -> {
144 if (!mUserManager.isUserUnlocked(uid)) {
145 logd("Showed unlock dialog for user: " + uid + " after " + delayMillis
146 + " delay.");
Erin Yan5eb60a22019-09-04 11:55:30 -0700147 mWindowManager.addView(mUnlockDialogLayout, mParams);
Erin Yan8182bfd2019-08-14 12:03:12 -0700148 }
149 }, delayMillis);
150 }
151 mIsDialogShowing = true;
152 }
153
154 private void setUid(int uid) {
155 mUid = uid;
Erin Yan5eb60a22019-09-04 11:55:30 -0700156 TextView userName = mUnlockDialogLayout.findViewById(R.id.user_name);
Erin Yan8182bfd2019-08-14 12:03:12 -0700157 userName.setText(mUserManager.getUserInfo(mUid).name);
Erin Yan5eb60a22019-09-04 11:55:30 -0700158 ImageView avatar = mUnlockDialogLayout.findViewById(R.id.avatar);
Erin Yan8182bfd2019-08-14 12:03:12 -0700159 avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
160 setButtonText();
161 }
162
Erin Yan5eb60a22019-09-04 11:55:30 -0700163 private void hideUnlockDialog(boolean dismissUserSwitcher) {
Erin Yan8182bfd2019-08-14 12:03:12 -0700164 if (!mIsDialogShowing) {
165 return;
166 }
Erin Yan5eb60a22019-09-04 11:55:30 -0700167 mWindowManager.removeView(mUnlockDialogLayout);
Erin Yan8182bfd2019-08-14 12:03:12 -0700168 logd("Receiver unregistered");
169 mContext.unregisterReceiver(this);
Erin Yan5eb60a22019-09-04 11:55:30 -0700170 if (mOnHideListener != null) {
171 mOnHideListener.onHide(dismissUserSwitcher);
Erin Yan8182bfd2019-08-14 12:03:12 -0700172 }
173 mIsDialogShowing = false;
174 }
175
176 @Override
177 public void onReceive(Context context, Intent intent) {
178 String action = intent.getAction();
179 if (action == null) {
180 return;
181 }
182 switch (action) {
183 case BluetoothAdapter.ACTION_BLE_STATE_CHANGED:
184 logd("Received ACTION_BLE_STATE_CHANGED");
185 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
186 if (state == BluetoothAdapter.STATE_BLE_ON) {
187 logd("Received BLE_ON");
188 mUnlockingText.setText(R.string.unlock_dialog_message_start);
189 }
190 break;
191 case Intent.ACTION_USER_UNLOCKED:
192 int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
193 if (uid == mUid) {
194 logd("IHU unlocked");
195 hideUnlockDialog(/* notifyOnHideListener= */false);
196 } else {
197 Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid);
198 }
199 break;
200 default:
201 Log.e(TAG, "Encountered unexpected action when attempting to set "
202 + "unlock state message: " + action);
203 }
204 }
205
Erin Yanb1af47b2019-08-29 16:00:47 -0700206 // Set button text based on screen lock type
Erin Yan8182bfd2019-08-14 12:03:12 -0700207 private void setButtonText() {
208 LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
209 int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid);
210 switch (passwordQuality) {
211 // PIN
Erin Yanb1af47b2019-08-29 16:00:47 -0700212 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
Erin Yan8182bfd2019-08-14 12:03:12 -0700213 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
Erin Yanb1af47b2019-08-29 16:00:47 -0700214 mButton.setText(R.string.unlock_dialog_button_text_pin);
215 break;
Erin Yan8182bfd2019-08-14 12:03:12 -0700216 // Pattern
217 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
218 mButton.setText(R.string.unlock_dialog_button_text_pattern);
219 break;
220 // Password
Erin Yanb1af47b2019-08-29 16:00:47 -0700221 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
222 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
223 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
Erin Yan8182bfd2019-08-14 12:03:12 -0700224 mButton.setText(R.string.unlock_dialog_button_text_password);
225 break;
226 default:
Erin Yanb1af47b2019-08-29 16:00:47 -0700227 Log.e(TAG, "Encountered unexpected screen lock type when attempting to set "
Erin Yan8182bfd2019-08-14 12:03:12 -0700228 + "button text:" + passwordQuality);
229 }
230 }
231
232 private WindowManager.LayoutParams createLayoutParams() {
233 return new WindowManager.LayoutParams(
234 WindowManager.LayoutParams.MATCH_PARENT,
235 WindowManager.LayoutParams.MATCH_PARENT,
236 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
237 WindowManager.LayoutParams.FLAG_FULLSCREEN
238 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
239 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
240 PixelFormat.TRANSLUCENT
241 );
242 }
243
244 private void logd(String message) {
245 if (Log.isLoggable(TAG, Log.DEBUG)) {
246 Log.d(TAG, message);
247 }
248 }
249
250 /**
251 * Listener used to notify when the dialog is hidden
252 */
253 interface OnHideListener {
Erin Yan5eb60a22019-09-04 11:55:30 -0700254 void onHide(boolean dismissUserSwitcher);
Erin Yan8182bfd2019-08-14 12:03:12 -0700255 }
256}