• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.car.settings.security;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.os.UserHandle;
22 import android.text.Editable;
23 import android.text.TextUtils;
24 import android.text.TextWatcher;
25 import android.view.View;
26 import android.view.inputmethod.EditorInfo;
27 import android.view.inputmethod.InputMethodManager;
28 import android.widget.EditText;
29 import android.widget.TextView;
30 
31 import androidx.annotation.LayoutRes;
32 import androidx.annotation.StringRes;
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.car.settings.R;
36 import com.android.car.settings.common.BaseFragment;
37 import com.android.internal.widget.LockscreenCredential;
38 import com.android.internal.widget.TextViewInputDisabler;
39 
40 /**
41  * Fragment for confirming existing lock PIN or password.  The containing activity must implement
42  * CheckLockListener.
43  */
44 public class ConfirmLockPinPasswordFragment extends BaseFragment {
45 
46     private static final String FRAGMENT_TAG_CHECK_LOCK_WORKER = "check_lock_worker";
47     private static final String EXTRA_IS_PIN = "extra_is_pin";
48 
49     private PinPadView mPinPad;
50     private EditText mPasswordField;
51     private TextView mMsgView;
52 
53     private CheckLockWorker mCheckLockWorker;
54     private CheckLockListener mCheckLockListener;
55 
56     private int mUserId;
57     private boolean mIsPin;
58     private LockscreenCredential mEnteredPassword;
59 
60     private ConfirmLockLockoutHelper mConfirmLockLockoutHelper;
61 
62     private TextViewInputDisabler mPasswordEntryInputDisabler;
63     private InputMethodManager mImm;
64 
65     /**
66      * Factory method for creating fragment in PIN mode.
67      */
newPinInstance()68     public static ConfirmLockPinPasswordFragment newPinInstance() {
69         ConfirmLockPinPasswordFragment patternFragment = new ConfirmLockPinPasswordFragment();
70         Bundle bundle = new Bundle();
71         bundle.putBoolean(EXTRA_IS_PIN, true);
72         patternFragment.setArguments(bundle);
73         return patternFragment;
74     }
75 
76     /**
77      * Factory method for creating fragment in password mode.
78      */
newPasswordInstance()79     public static ConfirmLockPinPasswordFragment newPasswordInstance() {
80         ConfirmLockPinPasswordFragment patternFragment = new ConfirmLockPinPasswordFragment();
81         Bundle bundle = new Bundle();
82         bundle.putBoolean(EXTRA_IS_PIN, false);
83         patternFragment.setArguments(bundle);
84         return patternFragment;
85     }
86 
87     @Override
88     @LayoutRes
getLayoutId()89     protected int getLayoutId() {
90         return mIsPin ? R.layout.confirm_lock_pin : R.layout.confirm_lock_password;
91     }
92 
93     @Override
94     @StringRes
getTitleId()95     protected int getTitleId() {
96         return R.string.security_settings_title;
97     }
98 
99     @Override
onAttach(Context context)100     public void onAttach(Context context) {
101         super.onAttach(context);
102         if ((getActivity() instanceof CheckLockListener)) {
103             mCheckLockListener = (CheckLockListener) getActivity();
104         } else {
105             throw new RuntimeException("The activity must implement CheckLockListener");
106         }
107 
108         mUserId = UserHandle.myUserId();
109         mConfirmLockLockoutHelper = ConfirmLockLockoutHelper.getInstance(requireContext(), mUserId);
110         mImm = requireContext().getSystemService(InputMethodManager.class);
111     }
112 
113     @Override
onCreate(Bundle savedInstanceState)114     public void onCreate(Bundle savedInstanceState) {
115         super.onCreate(savedInstanceState);
116         Bundle args = getArguments();
117         if (args != null) {
118             mIsPin = args.getBoolean(EXTRA_IS_PIN);
119         }
120     }
121 
122     @Override
onViewCreated(View view, Bundle savedInstanceState)123     public void onViewCreated(View view, Bundle savedInstanceState) {
124         super.onViewCreated(view, savedInstanceState);
125 
126         mPasswordField = view.findViewById(R.id.password_entry);
127         mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordField);
128         mMsgView = view.findViewById(R.id.message);
129         mConfirmLockLockoutHelper.setConfirmLockUIController(
130                 new ConfirmLockLockoutHelper.ConfirmLockUIController() {
131                     @Override
132                     public void setErrorText(String text) {
133                         mMsgView.setText(text);
134                     }
135 
136                     @Override
137                     public void refreshUI(boolean isLockedOut) {
138                         if (mIsPin) {
139                             updatePinEntry(isLockedOut);
140                         } else {
141                             updatePasswordEntry(isLockedOut);
142                         }
143                     }
144                 });
145 
146         if (mIsPin) {
147             initPinView(view);
148         } else {
149             initPasswordView();
150         }
151 
152         if (savedInstanceState != null) {
153             mCheckLockWorker = (CheckLockWorker) getFragmentManager().findFragmentByTag(
154                     FRAGMENT_TAG_CHECK_LOCK_WORKER);
155         }
156     }
157 
158     @Override
onStart()159     public void onStart() {
160         super.onStart();
161         if (mCheckLockWorker != null) {
162             mCheckLockWorker.setListener(this::onCheckCompleted);
163         }
164     }
165 
166     @Override
onResume()167     public void onResume() {
168         super.onResume();
169 
170         mConfirmLockLockoutHelper.onResumeUI();
171     }
172 
173     @Override
onPause()174     public void onPause() {
175         super.onPause();
176 
177         mConfirmLockLockoutHelper.onPauseUI();
178     }
179 
180     @Override
onStop()181     public void onStop() {
182         super.onStop();
183         if (mCheckLockWorker != null) {
184             mCheckLockWorker.setListener(null);
185         }
186     }
187 
188     @Override
onDestroy()189     public void onDestroy() {
190         super.onDestroy();
191         mPasswordField.setText(null);
192 
193         PasswordHelper.zeroizeCredentials(mEnteredPassword);
194     }
195 
initCheckLockWorker()196     private void initCheckLockWorker() {
197         if (mCheckLockWorker == null) {
198             mCheckLockWorker = new CheckLockWorker();
199             mCheckLockWorker.setListener(this::onCheckCompleted);
200 
201             getFragmentManager()
202                     .beginTransaction()
203                     .add(mCheckLockWorker, FRAGMENT_TAG_CHECK_LOCK_WORKER)
204                     .commitNow();
205         }
206     }
207 
getEnteredPassword()208     private LockscreenCredential getEnteredPassword() {
209         if (mIsPin) {
210             return LockscreenCredential.createPinOrNone(mPasswordField.getText());
211         } else {
212             return LockscreenCredential.createPasswordOrNone(mPasswordField.getText());
213         }
214     }
215 
initPinView(View view)216     private void initPinView(View view) {
217         mPinPad = view.findViewById(R.id.pin_pad);
218 
219         PinPadView.PinPadClickListener pinPadClickListener = new PinPadView.PinPadClickListener() {
220             @Override
221             public void onDigitKeyClick(String digit) {
222                 clearError();
223                 mPasswordField.append(digit);
224             }
225 
226             @Override
227             public void onBackspaceClick() {
228                 clearError();
229                 if (!TextUtils.isEmpty(mPasswordField.getText())) {
230                     mPasswordField.getText().delete(mPasswordField.getSelectionEnd() - 1,
231                             mPasswordField.getSelectionEnd());
232                 }
233             }
234 
235             @Override
236             public void onEnterKeyClick() {
237                 mEnteredPassword = getEnteredPassword();
238                 if (!mEnteredPassword.isNone()) {
239                     initCheckLockWorker();
240                     mPinPad.setEnabled(false);
241                     mCheckLockWorker.checkPinPassword(mUserId, mEnteredPassword);
242                 }
243             }
244         };
245 
246         mPinPad.setPinPadClickListener(pinPadClickListener);
247     }
248 
initPasswordView()249     private void initPasswordView() {
250         mPasswordField.setOnEditorActionListener((textView, actionId, keyEvent) -> {
251             // Check if this was the result of hitting the enter or "done" key.
252             if (actionId == EditorInfo.IME_NULL
253                     || actionId == EditorInfo.IME_ACTION_DONE
254                     || actionId == EditorInfo.IME_ACTION_NEXT) {
255 
256                 initCheckLockWorker();
257                 if (!mCheckLockWorker.isCheckInProgress()) {
258                     mEnteredPassword = getEnteredPassword();
259                     mCheckLockWorker.checkPinPassword(mUserId, mEnteredPassword);
260                 }
261                 return true;
262             }
263             return false;
264         });
265 
266         mPasswordField.addTextChangedListener(new TextWatcher() {
267             @Override
268             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
269             }
270 
271             @Override
272             public void onTextChanged(CharSequence s, int start, int before, int count) {
273             }
274 
275             @Override
276             public void afterTextChanged(Editable s) {
277                 clearError();
278             }
279         });
280     }
281 
clearError()282     private void clearError() {
283         if (!TextUtils.isEmpty(mMsgView.getText())) {
284             mMsgView.setText("");
285         }
286     }
287 
hideKeyboard()288     private void hideKeyboard() {
289         View currentFocus = getActivity().getCurrentFocus();
290         if (currentFocus == null) {
291             currentFocus = getActivity().getWindow().getDecorView();
292         }
293 
294         if (currentFocus != null) {
295             InputMethodManager inputMethodManager =
296                     (InputMethodManager) currentFocus.getContext()
297                             .getSystemService(Context.INPUT_METHOD_SERVICE);
298             inputMethodManager
299                     .hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
300         }
301     }
302 
303     @VisibleForTesting
onCheckCompleted(boolean lockMatched, int timeoutMs)304     void onCheckCompleted(boolean lockMatched, int timeoutMs) {
305         if (lockMatched) {
306             mCheckLockListener.onLockVerified(mEnteredPassword);
307         } else {
308             if (timeoutMs > 0) {
309                 mConfirmLockLockoutHelper.onCheckCompletedWithTimeout(timeoutMs);
310             } else {
311                 mMsgView.setText(
312                         mIsPin ? R.string.lockscreen_wrong_pin
313                                 : R.string.lockscreen_wrong_password);
314                 if (mIsPin) {
315                     mPinPad.setEnabled(true);
316                 }
317             }
318         }
319 
320         if (!mIsPin) {
321             hideKeyboard();
322         }
323     }
324 
updatePasswordEntry(boolean isLockedOut)325     private void updatePasswordEntry(boolean isLockedOut) {
326         mPasswordField.setEnabled(!isLockedOut);
327         mPasswordEntryInputDisabler.setInputEnabled(!isLockedOut);
328         if (isLockedOut) {
329             if (mImm != null) {
330                 mImm.hideSoftInputFromWindow(mPasswordField.getWindowToken(), /* flags= */ 0);
331             }
332         } else {
333             mPasswordField.requestFocus();
334         }
335     }
336 
updatePinEntry(boolean isLockedOut)337     private void updatePinEntry(boolean isLockedOut) {
338         mPinPad.setEnabled(!isLockedOut);
339         if (isLockedOut) {
340             if (mImm != null) {
341                 mImm.hideSoftInputFromWindow(mPinPad.getWindowToken(), /* flags= */ 0);
342             }
343         } else {
344             mPinPad.requestFocus();
345         }
346     }
347 }
348