From 73dcb47ecb679cc291ebbbfaeb0ea54aa7f41b88 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Wed, 28 Jun 2023 14:53:14 +0800 Subject: [PATCH 1/2] Extract common code to SaveChosenLockWorkerBase * Extract common code of ChooseLockPassword.SaveAndFinishWorker and ChooseLockPattern.SaveAndFinishWorker to the parent class. * Make setters return this to make it easy to chain setter calls. * Rename SaveChosenLockWorkerBase to SaveAndFinishWorker. This will make the code changes in the next CL much easier. Bug: 271968977 Bug: 277561275 Test: 1. Add screen lock (password/PIN/pattern) using Settings 2. check screen lock works correctly Change-Id: I98acd25f2dd81ab4608cc6943e4f238070003c17 --- .../settings/password/ChooseLockPassword.java | 54 +------------ .../settings/password/ChooseLockPattern.java | 55 +------------ .../password/ConfirmLockPassword.java | 10 +-- .../settings/password/ConfirmLockPattern.java | 10 +-- ...rkerBase.java => SaveAndFinishWorker.java} | 79 ++++++++++++++----- .../exempt_not_implementing_instrumentable | 3 +- 6 files changed, 79 insertions(+), 132 deletions(-) rename src/com/android/settings/password/{SaveChosenLockWorkerBase.java => SaveAndFinishWorker.java} (57%) diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index 16c4f5bb1ab..6d5ce905cff 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -65,7 +65,6 @@ import android.text.Spannable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; -import android.util.Pair; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -87,7 +86,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.PasswordValidationError; import com.android.internal.widget.TextViewInputDisabler; -import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SetupWizardUtils; @@ -1009,7 +1007,9 @@ public class ChooseLockPassword extends SettingsActivity { setNextEnabled(false); mSaveAndFinishWorker = new SaveAndFinishWorker(); - mSaveAndFinishWorker.setListener(this); + mSaveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); @@ -1029,7 +1029,7 @@ public class ChooseLockPassword extends SettingsActivity { (mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()), mUserId); - mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword, + mSaveAndFinishWorker.start(mLockPatternUtils, mChosenPassword, mCurrentCredential, mUserId); } @@ -1082,50 +1082,4 @@ public class ChooseLockPassword extends SettingsActivity { } } } - - public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { - - private LockscreenCredential mChosenPassword; - private LockscreenCredential mCurrentCredential; - - public void start(LockPatternUtils utils, boolean requestGatekeeperPassword, - LockscreenCredential chosenPassword, LockscreenCredential currentCredential, - int userId) { - prepare(utils, requestGatekeeperPassword, userId); - - mChosenPassword = chosenPassword; - mCurrentCredential = currentCredential != null ? currentCredential - : LockscreenCredential.createNone(); - mUserId = userId; - - start(); - } - - @Override - protected Pair saveAndVerifyInBackground() { - final boolean success = mUtils.setLockCredential( - mChosenPassword, mCurrentCredential, mUserId); - if (success) { - unifyProfileCredentialIfRequested(); - } - Intent result = null; - if (success && mRequestGatekeeperPassword) { - // If a Gatekeeper Password was requested, invoke the LockSettingsService code - // path to return a Gatekeeper Password based on the credential that the user - // chose. This should only be run if the credential was successfully set. - final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPassword, - mUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE); - - if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) { - Log.e(TAG, "critical: bad response or missing GK PW handle for known good" - + " password: " + response.toString()); - } - - result = new Intent(); - result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, - response.getGatekeeperPasswordHandle()); - } - return Pair.create(success, result); - } - } } diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index a5d04cc4715..e309a606abd 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -34,7 +34,6 @@ import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; -import android.util.Pair; import android.util.TypedValue; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -53,7 +52,6 @@ import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockPatternView.DisplayMode; import com.android.internal.widget.LockscreenCredential; -import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SetupWizardUtils; @@ -827,7 +825,9 @@ public class ChooseLockPattern extends SettingsActivity { setRightButtonEnabled(false); mSaveAndFinishWorker = new SaveAndFinishWorker(); - mSaveAndFinishWorker.setListener(this); + mSaveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); @@ -843,7 +843,7 @@ public class ChooseLockPattern extends SettingsActivity { profileCredential); } } - mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword, + mSaveAndFinishWorker.start(mLockPatternUtils, mChosenPattern, mCurrentCredential, mUserId); } @@ -867,51 +867,4 @@ public class ChooseLockPattern extends SettingsActivity { getActivity().finish(); } } - - public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { - - private LockscreenCredential mChosenPattern; - private LockscreenCredential mCurrentCredential; - - public void start(LockPatternUtils utils, boolean requestGatekeeperPassword, - LockscreenCredential chosenPattern, LockscreenCredential currentCredential, - int userId) { - prepare(utils, requestGatekeeperPassword, userId); - - mCurrentCredential = currentCredential != null ? currentCredential - : LockscreenCredential.createNone(); - mChosenPattern = chosenPattern; - mUserId = userId; - - start(); - } - - @Override - protected Pair saveAndVerifyInBackground() { - final int userId = mUserId; - final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential, - userId); - if (success) { - unifyProfileCredentialIfRequested(); - } - Intent result = null; - if (success && mRequestGatekeeperPassword) { - // If a Gatekeeper Password was requested, invoke the LockSettingsService code - // path to return a Gatekeeper Password based on the credential that the user - // chose. This should only be run if the credential was successfully set. - final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPattern, - userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE); - - if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) { - Log.e(TAG, "critical: bad response or missing GK PW handle for known good" - + " pattern: " + response.toString()); - } - - result = new Intent(); - result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, - response.getGatekeeperPasswordHandle()); - } - return Pair.create(success, result); - } - } } diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java index c6022b5d3ce..8d0ff14ae8e 100644 --- a/src/com/android/settings/password/ConfirmLockPassword.java +++ b/src/com/android/settings/password/ConfirmLockPassword.java @@ -125,7 +125,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment implements OnClickListener, OnEditorActionListener, - CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener, + CredentialCheckResultTracker.Listener, SaveAndFinishWorker.Listener, RemoteLockscreenValidationFragment.Listener { private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result"; private ImeAwareEditText mPasswordEntry; @@ -633,15 +633,15 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment .getLockscreenCredential() != null) { Log.i(TAG, "Setting device screen lock to the other device's screen lock."); - ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker = - new ChooseLockPassword.SaveAndFinishWorker(); + SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker(); getFragmentManager().beginTransaction().add(saveAndFinishWorker, null) .commit(); getFragmentManager().executePendingTransactions(); - saveAndFinishWorker.setListener(this); + saveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(true); saveAndFinishWorker.start( mLockPatternUtils, - /* requestGatekeeperPassword= */ true, mRemoteLockscreenValidationFragment.getLockscreenCredential(), /* currentCredential= */ null, mEffectiveUserId); diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java index a2bcb5af510..ffd7c64c5ea 100644 --- a/src/com/android/settings/password/ConfirmLockPattern.java +++ b/src/com/android/settings/password/ConfirmLockPattern.java @@ -93,7 +93,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment implements AppearAnimationCreator, CredentialCheckResultTracker.Listener, - SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener { + SaveAndFinishWorker.Listener, RemoteLockscreenValidationFragment.Listener { private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result"; @@ -630,15 +630,15 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment .getLockscreenCredential() != null) { Log.i(TAG, "Setting device screen lock to the other device's screen lock."); - ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker = - new ChooseLockPattern.SaveAndFinishWorker(); + SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker(); getFragmentManager().beginTransaction().add(saveAndFinishWorker, null) .commit(); getFragmentManager().executePendingTransactions(); - saveAndFinishWorker.setListener(this); + saveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(true); saveAndFinishWorker.start( mLockPatternUtils, - /* requestGatekeeperPassword= */ true, mRemoteLockscreenValidationFragment.getLockscreenCredential(), /* currentCredential= */ null, mEffectiveUserId); diff --git a/src/com/android/settings/password/SaveChosenLockWorkerBase.java b/src/com/android/settings/password/SaveAndFinishWorker.java similarity index 57% rename from src/com/android/settings/password/SaveChosenLockWorkerBase.java rename to src/com/android/settings/password/SaveAndFinishWorker.java index 48649412a9c..1af3b15d312 100644 --- a/src/com/android/settings/password/SaveChosenLockWorkerBase.java +++ b/src/com/android/settings/password/SaveAndFinishWorker.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; import android.util.Pair; import android.widget.Toast; @@ -27,6 +28,7 @@ import androidx.fragment.app.Fragment; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; +import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.R; import com.android.settings.safetycenter.LockScreenSafetySource; @@ -34,18 +36,21 @@ import com.android.settings.safetycenter.LockScreenSafetySource; * 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 { +public class SaveAndFinishWorker extends Fragment { + private static final String TAG = "SaveAndFinishWorker"; private Listener mListener; private boolean mFinished; private Intent mResultData; - protected LockPatternUtils mUtils; - protected boolean mRequestGatekeeperPassword; - protected boolean mWasSecureBefore; - protected int mUserId; - protected int mUnificationProfileId = UserHandle.USER_NULL; - protected LockscreenCredential mUnificationProfileCredential; + private LockPatternUtils mUtils; + private boolean mRequestGatekeeperPassword; + private boolean mWasSecureBefore; + private int mUserId; + private int mUnificationProfileId = UserHandle.USER_NULL; + private LockscreenCredential mUnificationProfileCredential; + private LockscreenCredential mChosenCredential; + private LockscreenCredential mCurrentCredential; private boolean mBlocking; @@ -55,28 +60,31 @@ abstract class SaveChosenLockWorkerBase extends Fragment { setRetainInstance(true); } - public void setListener(Listener listener) { + public SaveAndFinishWorker setListener(Listener listener) { if (mListener == listener) { - return; + return this; } mListener = listener; if (mFinished && mListener != null) { mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData); } + return this; } - protected void prepare(LockPatternUtils utils, boolean requestGatekeeperPassword, int userId) { + public void start(LockPatternUtils utils, LockscreenCredential chosenCredential, + LockscreenCredential currentCredential, int userId) { mUtils = utils; mUserId = userId; - mRequestGatekeeperPassword = requestGatekeeperPassword; // This will be a no-op for non managed profiles. mWasSecureBefore = mUtils.isSecure(mUserId); mFinished = false; mResultData = null; - } - protected void start() { + mChosenCredential = chosenCredential; + mCurrentCredential = currentCredential != null ? currentCredential + : LockscreenCredential.createNone(); + if (mBlocking) { finish(saveAndVerifyInBackground().second); } else { @@ -89,9 +97,34 @@ abstract class SaveChosenLockWorkerBase extends Fragment { * @return pair where the first is a boolean confirming whether the change was successful or not * and second is the Intent which has the challenge token or is null. */ - protected abstract Pair saveAndVerifyInBackground(); + private Pair saveAndVerifyInBackground() { + final int userId = mUserId; + final boolean success = mUtils.setLockCredential(mChosenCredential, mCurrentCredential, + userId); + if (success) { + unifyProfileCredentialIfRequested(); + } + Intent result = null; + if (success && mRequestGatekeeperPassword) { + // If a Gatekeeper Password was requested, invoke the LockSettingsService code + // path to return a Gatekeeper Password based on the credential that the user + // chose. This should only be run if the credential was successfully set. + final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential, + userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE); - protected void finish(Intent resultData) { + if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) { + Log.e(TAG, "critical: bad response or missing GK PW handle for known good" + + " credential: " + response.toString()); + } + + result = new Intent(); + result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, + response.getGatekeeperPasswordHandle()); + } + return Pair.create(success, result); + } + + private void finish(Intent resultData) { mFinished = true; mResultData = resultData; if (mListener != null) { @@ -103,16 +136,24 @@ abstract class SaveChosenLockWorkerBase extends Fragment { LockScreenSafetySource.onLockScreenChange(getContext()); } - public void setBlocking(boolean blocking) { - mBlocking = blocking; + public SaveAndFinishWorker setRequestGatekeeperPasswordHandle(boolean value) { + mRequestGatekeeperPassword = value; + return this; } - public void setProfileToUnify(int profileId, LockscreenCredential credential) { + public SaveAndFinishWorker setBlocking(boolean blocking) { + mBlocking = blocking; + return this; + } + + public SaveAndFinishWorker setProfileToUnify( + int profileId, LockscreenCredential credential) { mUnificationProfileId = profileId; mUnificationProfileCredential = credential.duplicate(); + return this; } - protected void unifyProfileCredentialIfRequested() { + private void unifyProfileCredentialIfRequested() { if (mUnificationProfileId != UserHandle.USER_NULL) { mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false, mUnificationProfileCredential); diff --git a/tests/robotests/assets/exempt_not_implementing_instrumentable b/tests/robotests/assets/exempt_not_implementing_instrumentable index 04ef0ef9594..28e1e7382b1 100644 --- a/tests/robotests/assets/exempt_not_implementing_instrumentable +++ b/tests/robotests/assets/exempt_not_implementing_instrumentable @@ -1,8 +1,7 @@ com.android.settings.deletionhelper.ActivationWarningFragment com.android.settings.applications.appops.AppOpsCategory com.android.settings.CustomListPreference$CustomListPreferenceDialogFragment -com.android.settings.password.ChooseLockPassword$SaveAndFinishWorker -com.android.settings.password.ChooseLockPattern$SaveAndFinishWorker +com.android.settings.password.SaveAndFinishWorker com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog com.android.settings.password.CredentialCheckResultTracker From c726bd48b44337bab10b0978c890e6d15e212066 Mon Sep 17 00:00:00 2001 From: Chun-Wei Wang Date: Mon, 10 Jul 2023 23:22:32 +0000 Subject: [PATCH 2/2] Handle EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW So the new password can be saved per caller's request. This will remove the additional step to ask the user to enter the new credential again and thus simplifying the UI flow. Bug: 271968977 Bug: 277561275 Test: atest SettingsUnitTests:SaveAndFinishWorkerTest Test: atest ChooseLockPasswordTest Change-Id: I20232619225b17edda0a72dad43b120d5a249203 --- .../settings/password/ChooseLockGeneric.java | 4 + .../settings/password/ChooseLockPassword.java | 7 +- .../settings/password/ChooseLockPattern.java | 7 +- .../password/ChooseLockSettingsHelper.java | 2 + .../password/SaveAndFinishWorker.java | 70 ++++++--- .../password/SaveAndFinishWorkerTest.java | 136 ++++++++++++++++++ 6 files changed, 204 insertions(+), 22 deletions(-) create mode 100644 tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index 4c4795cbd84..0bf1255b3b9 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -33,6 +33,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY; +import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW; import android.app.Activity; import android.app.Dialog; @@ -795,6 +796,9 @@ public class ChooseLockGeneric extends SettingsActivity { if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) { intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped); } + if (getIntent().getBooleanExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false)) { + intent.putExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, true); + } intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras()); // If the caller requested Gatekeeper Password Handle to be returned, we assume it // came from biometric enrollment. onActivityResult will put the LockSettingsService diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index 6d5ce905cff..f8f4345f893 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -232,6 +232,7 @@ public class ChooseLockPassword extends SettingsActivity { private LockscreenCredential mCurrentCredential; private LockscreenCredential mChosenPassword; private boolean mRequestGatekeeperPassword; + private boolean mRequestWriteRepairModePassword; private ImeAwareEditText mPasswordEntry; private TextViewInputDisabler mPasswordEntryInputDisabler; @@ -559,6 +560,8 @@ public class ChooseLockPassword extends SettingsActivity { ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); mRequestGatekeeperPassword = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false); + mRequestWriteRepairModePassword = intent.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false); if (savedInstanceState == null) { updateStage(Stage.Introduction); if (confirmCredentials) { @@ -568,6 +571,7 @@ public class ChooseLockPassword extends SettingsActivity { .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title)) .setReturnCredentials(true) .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword) .setUserId(mUserId) .show(); } @@ -1009,7 +1013,8 @@ public class ChooseLockPassword extends SettingsActivity { mSaveAndFinishWorker = new SaveAndFinishWorker(); mSaveAndFinishWorker .setListener(this) - .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword); + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index e309a606abd..7569c1596ec 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -204,6 +204,7 @@ public class ChooseLockPattern extends SettingsActivity { private LockscreenCredential mCurrentCredential; private boolean mRequestGatekeeperPassword; + private boolean mRequestWriteRepairModePassword; protected TextView mHeaderText; protected LockPatternView mLockPatternView; protected TextView mFooterText; @@ -561,6 +562,8 @@ public class ChooseLockPattern extends SettingsActivity { intent.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); mRequestGatekeeperPassword = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false); + mRequestWriteRepairModePassword = intent.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false); if (savedInstanceState == null) { if (confirmCredentials) { @@ -574,6 +577,7 @@ public class ChooseLockPattern extends SettingsActivity { .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title)) .setReturnCredentials(true) .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword) .setUserId(mUserId) .show(); @@ -827,7 +831,8 @@ public class ChooseLockPattern extends SettingsActivity { mSaveAndFinishWorker = new SaveAndFinishWorker(); mSaveAndFinishWorker .setListener(this) - .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword); + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java index 9533314c8a2..e5fc5507815 100644 --- a/src/com/android/settings/password/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java @@ -73,6 +73,8 @@ public final class ChooseLockSettingsHelper { public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"; public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW = "request_write_repair_mode_pw"; + public static final String EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL = + "wrote_repair_mode_credential"; /** * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are diff --git a/src/com/android/settings/password/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java index 1af3b15d312..df679e5f6cd 100644 --- a/src/com/android/settings/password/SaveAndFinishWorker.java +++ b/src/com/android/settings/password/SaveAndFinishWorker.java @@ -24,6 +24,7 @@ import android.util.Log; import android.util.Pair; import android.widget.Toast; +import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; import com.android.internal.widget.LockPatternUtils; @@ -45,6 +46,7 @@ public class SaveAndFinishWorker extends Fragment { private LockPatternUtils mUtils; private boolean mRequestGatekeeperPassword; + private boolean mRequestWriteRepairModePassword; private boolean mWasSecureBefore; private int mUserId; private int mUnificationProfileId = UserHandle.USER_NULL; @@ -72,7 +74,8 @@ public class SaveAndFinishWorker extends Fragment { return this; } - public void start(LockPatternUtils utils, LockscreenCredential chosenCredential, + @VisibleForTesting + void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential, LockscreenCredential currentCredential, int userId) { mUtils = utils; mUserId = userId; @@ -84,7 +87,11 @@ public class SaveAndFinishWorker extends Fragment { mChosenCredential = chosenCredential; mCurrentCredential = currentCredential != null ? currentCredential : LockscreenCredential.createNone(); + } + public void start(LockPatternUtils utils, LockscreenCredential chosenCredential, + LockscreenCredential currentCredential, int userId) { + prepare(utils, chosenCredential, currentCredential, userId); if (mBlocking) { finish(saveAndVerifyInBackground().second); } else { @@ -97,31 +104,49 @@ public class SaveAndFinishWorker extends Fragment { * @return pair where the first is a boolean confirming whether the change was successful or not * and second is the Intent which has the challenge token or is null. */ - private Pair saveAndVerifyInBackground() { + @VisibleForTesting + Pair saveAndVerifyInBackground() { final int userId = mUserId; - final boolean success = mUtils.setLockCredential(mChosenCredential, mCurrentCredential, - userId); - if (success) { - unifyProfileCredentialIfRequested(); + if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) { + return Pair.create(false, null); } - Intent result = null; - if (success && mRequestGatekeeperPassword) { + + unifyProfileCredentialIfRequested(); + + @LockPatternUtils.VerifyFlag int flags = 0; + if (mRequestGatekeeperPassword) { // If a Gatekeeper Password was requested, invoke the LockSettingsService code // path to return a Gatekeeper Password based on the credential that the user // chose. This should only be run if the credential was successfully set. - final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential, - userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE); - - if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) { - Log.e(TAG, "critical: bad response or missing GK PW handle for known good" - + " credential: " + response.toString()); - } - - result = new Intent(); - result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, - response.getGatekeeperPasswordHandle()); + flags |= LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE; } - return Pair.create(success, result); + if (mRequestWriteRepairModePassword) { + flags |= LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; + } + if (flags == 0) { + return Pair.create(true, null); + } + + Intent result = new Intent(); + final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential, + userId, flags); + if (response.isMatched()) { + if (mRequestGatekeeperPassword && response.containsGatekeeperPasswordHandle()) { + result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, + response.getGatekeeperPasswordHandle()); + } else if (mRequestGatekeeperPassword) { + Log.e(TAG, "critical: missing GK PW handle for known good credential: " + response); + } + } else { + Log.e(TAG, "critical: bad response for known good credential: " + response); + } + if (mRequestWriteRepairModePassword) { + // Notify the caller if repair mode credential is saved successfully + result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, + response.isMatched()); + } + + return Pair.create(true, result); } private void finish(Intent resultData) { @@ -141,6 +166,11 @@ public class SaveAndFinishWorker extends Fragment { return this; } + public SaveAndFinishWorker setRequestWriteRepairModePassword(boolean value) { + mRequestWriteRepairModePassword = value; + return this; + } + public SaveAndFinishWorker setBlocking(boolean blocking) { mBlocking = blocking; return this; diff --git a/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java new file mode 100644 index 00000000000..88e31508155 --- /dev/null +++ b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 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.password; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; +import com.android.internal.widget.VerifyCredentialResponse; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SaveAndFinishWorkerTest { + @Test + public void testSetRequestWriteRepairModePassword_setLockCredentialFail() { + int userId = 0; + int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; + var chosenCredential = LockscreenCredential.createPassword("1234"); + var currentCredential = LockscreenCredential.createNone(); + var worker = new SaveAndFinishWorker(); + var lpu = mock(LockPatternUtils.class); + + when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(false); + + worker.setRequestWriteRepairModePassword(true); + worker.prepare(lpu, chosenCredential, currentCredential, userId); + var result = worker.saveAndVerifyInBackground(); + + verify(lpu).setLockCredential(chosenCredential, currentCredential, userId); + verify(lpu, never()).verifyCredential(chosenCredential, userId, flags); + assertThat(result.first).isFalse(); + } + + @Test + public void testSetRequestWriteRepairModePassword_verifyCredentialFail() { + int userId = 0; + int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; + var chosenCredential = LockscreenCredential.createPassword("1234"); + var currentCredential = LockscreenCredential.createNone(); + var worker = new SaveAndFinishWorker(); + var lpu = mock(LockPatternUtils.class); + var response = VerifyCredentialResponse.fromError(); + + when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true); + when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response); + + worker.setRequestWriteRepairModePassword(true); + worker.prepare(lpu, chosenCredential, currentCredential, userId); + var result = worker.saveAndVerifyInBackground(); + + verify(lpu).setLockCredential(chosenCredential, currentCredential, userId); + verify(lpu).verifyCredential(chosenCredential, userId, flags); + assertThat(result.first).isTrue(); + assertThat(result.second.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, true)) + .isFalse(); + } + + @Test + public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed() { + int userId = 0; + int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; + var chosenCredential = LockscreenCredential.createPassword("1234"); + var currentCredential = LockscreenCredential.createNone(); + var worker = new SaveAndFinishWorker(); + var lpu = mock(LockPatternUtils.class); + var response = new VerifyCredentialResponse.Builder().build(); + + when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true); + when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response); + + worker.setRequestWriteRepairModePassword(true); + worker.prepare(lpu, chosenCredential, currentCredential, userId); + var result = worker.saveAndVerifyInBackground(); + + verify(lpu).setLockCredential(chosenCredential, currentCredential, userId); + verify(lpu).verifyCredential(chosenCredential, userId, flags); + assertThat(result.first).isTrue(); + assertThat(result.second.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false)) + .isTrue(); + } + + @Test + public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed_noGkPwHandle() { + int userId = 0; + int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW + | LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE; + var chosenCredential = LockscreenCredential.createPassword("1234"); + var currentCredential = LockscreenCredential.createNone(); + var worker = new SaveAndFinishWorker(); + var lpu = mock(LockPatternUtils.class); + var response = new VerifyCredentialResponse.Builder().build(); + + when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true); + when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response); + + worker.setRequestWriteRepairModePassword(true); + worker.setRequestGatekeeperPasswordHandle(true); + worker.prepare(lpu, chosenCredential, currentCredential, userId); + var result = worker.saveAndVerifyInBackground(); + + verify(lpu).setLockCredential(chosenCredential, currentCredential, userId); + verify(lpu).verifyCredential(chosenCredential, userId, flags); + assertThat(result.first).isTrue(); + assertThat(result.second.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false)) + .isTrue(); + assertThat(result.second.getLongExtra( + ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, -1)) + .isEqualTo(-1); + } +}