Merge "Fix ChoosePat/Pin/Pwd crash from async task" into mnc-dr-dev

This commit is contained in:
Xiyuan Xia
2015-08-28 01:46:57 +00:00
committed by Android (Google) Code Review
3 changed files with 301 additions and 181 deletions

View File

@@ -17,11 +17,11 @@
package com.android.settings;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
import com.android.settings.notification.RedactionInterstitial;
import android.app.Activity;
@@ -30,7 +30,6 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.inputmethodservice.KeyboardView;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -122,10 +121,12 @@ public class ChooseLockPassword extends SettingsActivity {
}
public static class ChooseLockPasswordFragment extends InstrumentedFragment
implements OnClickListener, OnEditorActionListener, TextWatcher {
implements OnClickListener, OnEditorActionListener, TextWatcher,
SaveAndFinishWorker.Listener {
private static final String KEY_FIRST_PIN = "first_pin";
private static final String KEY_UI_STAGE = "ui_stage";
private static final String KEY_CURRENT_PASSWORD = "current_password";
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
private String mCurrentPassword;
private String mChosenPassword;
@@ -142,14 +143,11 @@ public class ChooseLockPassword extends SettingsActivity {
private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0;
private LockPatternUtils mLockPatternUtils;
private AsyncTask<?, ?, ?> mPendingLockCheck;
private SaveAndFinishWorker mSaveAndFinishWorker;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private Stage mUiStage = Stage.Introduction;
// True once we have confirmed new PIN/password to prevent virtual keyboard
// re-entries of the same PIN
private boolean mDone = false;
private TextView mHeaderText;
private String mFirstPin;
private KeyboardView mKeyboardView;
@@ -305,8 +303,11 @@ public class ChooseLockPassword extends SettingsActivity {
if (mCurrentPassword == null) {
mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD);
}
// Re-attach to the exiting worker if there is one.
mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
FRAGMENT_TAG_SAVE_AND_FINISH);
}
mDone = false;
if (activity instanceof SettingsActivity) {
final SettingsActivity sa = (SettingsActivity) activity;
int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
@@ -325,16 +326,18 @@ public class ChooseLockPassword extends SettingsActivity {
public void onResume() {
super.onResume();
updateStage(mUiStage);
mPasswordEntryInputDisabler.setInputEnabled(true);
mKeyboardView.requestFocus();
if (mSaveAndFinishWorker != null) {
mSaveAndFinishWorker.setListener(this);
} else {
mKeyboardView.requestFocus();
}
}
@Override
public void onPause() {
mHandler.removeMessages(MSG_SHOW_ERROR);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
mPendingLockCheck = null;
if (mSaveAndFinishWorker != null) {
mSaveAndFinishWorker.setListener(null);
}
super.onPause();
@@ -482,39 +485,8 @@ public class ChooseLockPassword extends SettingsActivity {
return null;
}
private class SaveChosenPasswordAndFinish extends AsyncTask<Void, Void, Void> {
boolean mWasSecureBefore;
@Override
public void onPreExecute() {
mWasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId());
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
mLockPatternUtils.setCredentialRequiredToDecrypt(required);
}
@Override
public Void doInBackground(Void... v) {
mLockPatternUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality,
UserHandle.myUserId());
return null;
}
@Override
public void onPostExecute(Void v) {
if (mHasChallenge) {
startVerifyPassword(mChosenPassword, mWasSecureBefore);
return;
} else {
getActivity().setResult(RESULT_FINISHED);
}
finishConfirmStage(mWasSecureBefore);
}
}
public void handleNext() {
if (mDone) return;
if (mSaveAndFinishWorker != null) return;
mChosenPassword = mPasswordEntry.getText().toString();
if (TextUtils.isEmpty(mChosenPassword)) {
return;
@@ -529,9 +501,7 @@ public class ChooseLockPassword extends SettingsActivity {
}
} else if (mUiStage == Stage.NeedToConfirm) {
if (mFirstPin.equals(mChosenPassword)) {
setNextEnabled(false);
mDone = true;
new SaveChosenPasswordAndFinish().execute();
startSaveAndFinish();
} else {
CharSequence tmp = mPasswordEntry.getText();
if (tmp != null) {
@@ -545,49 +515,6 @@ public class ChooseLockPassword extends SettingsActivity {
}
}
private void startVerifyPassword(final String pin, final boolean wasSecureBefore) {
mPasswordEntryInputDisabler.setInputEnabled(false);
setNextEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
mPendingLockCheck = LockPatternChecker.verifyPassword(
mLockPatternUtils,
pin,
mChallenge,
UserHandle.myUserId(),
new LockPatternChecker.OnVerifyCallback() {
@Override
public void onVerified(byte[] token, int timeoutMs) {
if (token == null) {
Log.e(TAG, "critical: no token returned from known good password");
}
mPasswordEntryInputDisabler.setInputEnabled(true);
setNextEnabled(true);
mPendingLockCheck = null;
Intent intent = new Intent();
intent.putExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
token);
getActivity().setResult(RESULT_FINISHED, intent);
finishConfirmStage(wasSecureBefore);
}
});
}
private void finishConfirmStage(boolean wasSecureBefore) {
getActivity().finish();
if (!wasSecureBefore) {
Intent intent = getRedactionInterstitialIntent(getActivity());
if (intent != null) {
startActivity(intent);
}
}
}
protected void setNextEnabled(boolean enabled) {
mNextButton.setEnabled(enabled);
}
@@ -631,6 +558,7 @@ public class ChooseLockPassword extends SettingsActivity {
* Update the hint based on current Stage and length of password entry
*/
private void updateUi() {
final boolean canInput = mSaveAndFinishWorker == null;
String password = mPasswordEntry.getText().toString();
final int length = password.length();
if (mUiStage == Stage.Introduction) {
@@ -651,9 +579,10 @@ public class ChooseLockPassword extends SettingsActivity {
}
} else {
mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
setNextEnabled(length > 0);
setNextEnabled(canInput && length > 0);
}
setNextText(mUiStage.buttonText);
mPasswordEntryInputDisabler.setInputEnabled(canInput);
}
public void afterTextChanged(Editable s) {
@@ -671,5 +600,83 @@ public class ChooseLockPassword extends SettingsActivity {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
private void startSaveAndFinish() {
if (mSaveAndFinishWorker != null) {
Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
return;
}
mPasswordEntryInputDisabler.setInputEnabled(false);
setNextEnabled(false);
mSaveAndFinishWorker = new SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
mSaveAndFinishWorker.setListener(this);
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
mSaveAndFinishWorker.start(mLockPatternUtils, required, mHasChallenge, mChallenge,
mChosenPassword, mCurrentPassword, mRequestedQuality);
}
@Override
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
getActivity().setResult(RESULT_FINISHED, resultData);
getActivity().finish();
if (!wasSecureBefore) {
Intent intent = getRedactionInterstitialIntent(getActivity());
if (intent != null) {
startActivity(intent);
}
}
}
}
private static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
private String mChosenPassword;
private String mCurrentPassword;
private int mRequestedQuality;
public void start(LockPatternUtils utils, boolean required,
boolean hasChallenge, long challenge,
String chosenPassword, String currentPassword, int requestedQuality) {
prepare(utils, required, hasChallenge, challenge);
mChosenPassword = chosenPassword;
mCurrentPassword = currentPassword;
mRequestedQuality = requestedQuality;
start();
}
@Override
protected Intent saveAndVerifyInBackground() {
Intent result = null;
final int userId = UserHandle.myUserId();
mUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality,
userId);
if (mHasChallenge) {
byte[] token;
try {
token = mUtils.verifyPassword(mChosenPassword, mChallenge, userId);
} catch (RequestThrottledException e) {
token = null;
}
if (token == null) {
Log.e(TAG, "critical: no token returned for known good password.");
}
result = new Intent();
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
return result;
}
}
}

View File

@@ -19,8 +19,8 @@ package com.android.settings;
import com.android.internal.logging.MetricsLogger;
import com.google.android.collect.Lists;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell;
import com.android.settings.notification.RedactionInterstitial;
@@ -31,7 +31,6 @@ import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
@@ -125,7 +124,7 @@ public class ChooseLockPattern extends SettingsActivity {
}
public static class ChooseLockPatternFragment extends InstrumentedFragment
implements View.OnClickListener {
implements View.OnClickListener, SaveAndFinishWorker.Listener {
public static final int CONFIRM_EXISTING_REQUEST = 55;
@@ -137,6 +136,8 @@ public class ChooseLockPattern extends SettingsActivity {
private static final int ID_EMPTY_MESSAGE = -1;
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
private String mCurrentPattern;
private boolean mHasChallenge;
private long mChallenge;
@@ -354,7 +355,7 @@ public class ChooseLockPattern extends SettingsActivity {
};
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private AsyncTask<?, ?, ?> mPendingLockCheck;
private SaveAndFinishWorker mSaveAndFinishWorker;
private static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
@@ -433,21 +434,29 @@ public class ChooseLockPattern extends SettingsActivity {
mCurrentPattern = savedInstanceState.getString(KEY_CURRENT_PATTERN);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
// Re-attach to the exiting worker if there is one.
mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
FRAGMENT_TAG_SAVE_AND_FINISH);
}
}
@Override
public void onResume() {
super.onResume();
mLockPatternView.enableInput();
if (mSaveAndFinishWorker != null) {
setRightButtonEnabled(false);
mSaveAndFinishWorker.setListener(this);
} else {
mLockPatternView.enableInput();
}
}
@Override
public void onPause() {
super.onPause();
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
mPendingLockCheck = null;
if (mSaveAndFinishWorker != null) {
mSaveAndFinishWorker.setListener(null);
}
}
@@ -481,7 +490,7 @@ public class ChooseLockPattern extends SettingsActivity {
throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+ " when button is " + RightButtonMode.Confirm);
}
new SaveChosenPatternAndFinish().execute();
startSaveAndFinish();
} else if (mUiStage.rightMode == RightButtonMode.Ok) {
if (mUiStage != Stage.HelpScreen) {
throw new IllegalStateException("Help screen is only mode with ok button, "
@@ -568,7 +577,7 @@ public class ChooseLockPattern extends SettingsActivity {
setRightButtonText(stage.rightMode.text);
setRightButtonEnabled(stage.rightMode.enabled);
// same for whether the patten is enabled
// same for whether the pattern is enabled
if (stage.patternEnabled) {
mLockPatternView.enableInput();
} else {
@@ -613,7 +622,6 @@ public class ChooseLockPattern extends SettingsActivity {
}
}
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
@@ -621,91 +629,90 @@ public class ChooseLockPattern extends SettingsActivity {
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
private class SaveChosenPatternAndFinish extends AsyncTask<Void,Void,Void> {
boolean mLockVirgin;
LockPatternUtils mUtils;
boolean mWasSecureBefore;
private void startSaveAndFinish() {
if (mSaveAndFinishWorker != null) {
Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
return;
}
@Override
protected void onPreExecute(){
setRightButtonEnabled(false);
mUtils = mChooseLockSettingsHelper.utils();
mLockVirgin = !mUtils.isPatternEverChosen(UserHandle.myUserId());
setRightButtonEnabled(false);
mWasSecureBefore = mUtils.isSecure(UserHandle.myUserId());
mSaveAndFinishWorker = new SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
mSaveAndFinishWorker.setListener(this);
final boolean required = getActivity().getIntent().getBooleanExtra(
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
mUtils.setCredentialRequiredToDecrypt(required);
}
@Override
protected Void doInBackground(Void... params){
mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, UserHandle.myUserId());
return null;
}
@Override
protected void onPostExecute(Void param) {
if (mLockVirgin) {
mUtils.setVisiblePatternEnabled(true, UserHandle.myUserId());
}
if (mHasChallenge) {
startVerifyPattern(mUtils, mWasSecureBefore);
} else {
if (!mWasSecureBefore) {
Intent intent = getRedactionInterstitialIntent(getActivity());
if (intent != null) {
startActivity(intent);
}
}
getActivity().setResult(RESULT_FINISHED);
doFinish();
}
}
mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required,
mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern);
}
private void startVerifyPattern(LockPatternUtils utils, final boolean wasSecureBefore) {
mLockPatternView.disableInput();
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
mPendingLockCheck = LockPatternChecker.verifyPattern(
utils,
mChosenPattern,
mChallenge,
UserHandle.myUserId(),
new LockPatternChecker.OnVerifyCallback() {
@Override
public void onVerified(byte[] token, int timeoutMs) {
if (token == null) {
Log.e(TAG, "critical: no token returned for known good pattern");
}
mLockPatternView.enableInput();
mPendingLockCheck = null;
if (!wasSecureBefore) {
Intent intent = getRedactionInterstitialIntent(getActivity());
if (intent != null) {
startActivity(intent);
}
}
Intent intent = new Intent();
intent.putExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
getActivity().setResult(RESULT_FINISHED, intent);
doFinish();
}
});
}
private void doFinish() {
@Override
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
getActivity().setResult(RESULT_FINISHED, resultData);
getActivity().finish();
if (!wasSecureBefore) {
Intent intent = getRedactionInterstitialIntent(getActivity());
if (intent != null) {
startActivity(intent);
}
}
}
}
private static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
private List<LockPatternView.Cell> mChosenPattern;
private String mCurrentPattern;
private boolean mLockVirgin;
public void start(LockPatternUtils utils, boolean credentialRequired,
boolean hasChallenge, long challenge,
List<LockPatternView.Cell> chosenPattern, String currentPattern) {
prepare(utils, credentialRequired, hasChallenge, challenge);
mCurrentPattern = currentPattern;
mChosenPattern = chosenPattern;
mLockVirgin = !mUtils.isPatternEverChosen(UserHandle.myUserId());
start();
}
@Override
protected Intent saveAndVerifyInBackground() {
Intent result = null;
final int userId = UserHandle.myUserId();
mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
if (mHasChallenge) {
byte[] token;
try {
token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId);
} catch (RequestThrottledException e) {
token = null;
}
if (token == null) {
Log.e(TAG, "critical: no token returned for known good pattern");
}
result = new Intent();
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
return result;
}
@Override
protected void finish(Intent resultData) {
if (mLockVirgin) {
mUtils.setVisiblePatternEnabled(true, UserHandle.myUserId());
}
super.finish(resultData);
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.app.Fragment;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.internal.widget.LockPatternUtils;
/**
* An invisible retained worker fragment to track the AsyncWork that saves (and optionally
* verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
*/
abstract class SaveChosenLockWorkerBase extends Fragment {
private Listener mListener;
private boolean mFinished;
private Intent mResultData;
protected LockPatternUtils mUtils;
protected boolean mHasChallenge;
protected long mChallenge;
protected boolean mWasSecureBefore;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setListener(Listener listener) {
if (mListener == listener) {
return;
}
mListener = listener;
if (mFinished && mListener != null) {
mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
}
}
protected void prepare(LockPatternUtils utils, boolean credentialRequired,
boolean hasChallenge, long challenge) {
mUtils = utils;
mHasChallenge = hasChallenge;
mChallenge = challenge;
mWasSecureBefore = mUtils.isSecure(UserHandle.myUserId());
mUtils.setCredentialRequiredToDecrypt(credentialRequired);
mFinished = false;
mResultData = null;
}
protected void start() {
new Task().execute();
}
/**
* Executes the save and verify work in background.
* @return Intent with challenge token or null.
*/
protected abstract Intent saveAndVerifyInBackground();
protected void finish(Intent resultData) {
mFinished = true;
mResultData = resultData;
if (mListener != null) {
mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
}
}
private class Task extends AsyncTask<Void, Void, Intent> {
@Override
protected Intent doInBackground(Void... params){
return saveAndVerifyInBackground();
}
@Override
protected void onPostExecute(Intent resultData) {
finish(resultData);
}
}
interface Listener {
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
}
}