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 1bd3618f7be..09d192412ba 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; @@ -89,7 +88,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; @@ -236,6 +234,7 @@ public class ChooseLockPassword extends SettingsActivity { private LockscreenCredential mCurrentCredential; private LockscreenCredential mChosenPassword; private boolean mRequestGatekeeperPassword; + private boolean mRequestWriteRepairModePassword; private ImeAwareEditText mPasswordEntry; private TextViewInputDisabler mPasswordEntryInputDisabler; @@ -565,6 +564,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) { @@ -574,6 +575,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(); } @@ -1035,7 +1037,10 @@ public class ChooseLockPassword extends SettingsActivity { setNextEnabled(false); mSaveAndFinishWorker = new SaveAndFinishWorker(); - mSaveAndFinishWorker.setListener(this); + mSaveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); @@ -1055,7 +1060,7 @@ public class ChooseLockPassword extends SettingsActivity { (mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()), mUserId); - mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword, + mSaveAndFinishWorker.start(mLockPatternUtils, mChosenPassword, mCurrentCredential, mUserId); } @@ -1108,50 +1113,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..7569c1596ec 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; @@ -206,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; @@ -563,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) { @@ -576,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 +829,10 @@ public class ChooseLockPattern extends SettingsActivity { setRightButtonEnabled(false); mSaveAndFinishWorker = new SaveAndFinishWorker(); - mSaveAndFinishWorker.setListener(this); + mSaveAndFinishWorker + .setListener(this) + .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword) + .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword); getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, FRAGMENT_TAG_SAVE_AND_FINISH).commit(); @@ -843,7 +848,7 @@ public class ChooseLockPattern extends SettingsActivity { profileCredential); } } - mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword, + mSaveAndFinishWorker.start(mLockPatternUtils, mChosenPattern, mCurrentCredential, mUserId); } @@ -867,51 +872,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/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/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/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java new file mode 100644 index 00000000000..df679e5f6cd --- /dev/null +++ b/src/com/android/settings/password/SaveAndFinishWorker.java @@ -0,0 +1,213 @@ +/* + * 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.password; + +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; + +import androidx.annotation.VisibleForTesting; +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; + +/** + * 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). + */ +public class SaveAndFinishWorker extends Fragment { + private static final String TAG = "SaveAndFinishWorker"; + + private Listener mListener; + private boolean mFinished; + private Intent mResultData; + + private LockPatternUtils mUtils; + private boolean mRequestGatekeeperPassword; + private boolean mRequestWriteRepairModePassword; + private boolean mWasSecureBefore; + private int mUserId; + private int mUnificationProfileId = UserHandle.USER_NULL; + private LockscreenCredential mUnificationProfileCredential; + private LockscreenCredential mChosenCredential; + private LockscreenCredential mCurrentCredential; + + private boolean mBlocking; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + public SaveAndFinishWorker setListener(Listener listener) { + if (mListener == listener) { + return this; + } + + mListener = listener; + if (mFinished && mListener != null) { + mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData); + } + return this; + } + + @VisibleForTesting + void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential, + LockscreenCredential currentCredential, int userId) { + mUtils = utils; + mUserId = userId; + // This will be a no-op for non managed profiles. + mWasSecureBefore = mUtils.isSecure(mUserId); + mFinished = false; + mResultData = null; + + 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 { + new Task().execute(); + } + } + + /** + * Executes the save and verify work in background. + * @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. + */ + @VisibleForTesting + Pair saveAndVerifyInBackground() { + final int userId = mUserId; + if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) { + return Pair.create(false, null); + } + + 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. + flags |= LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE; + } + 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) { + mFinished = true; + mResultData = resultData; + if (mListener != null) { + mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData); + } + if (mUnificationProfileCredential != null) { + mUnificationProfileCredential.zeroize(); + } + LockScreenSafetySource.onLockScreenChange(getContext()); + } + + public SaveAndFinishWorker setRequestGatekeeperPasswordHandle(boolean value) { + mRequestGatekeeperPassword = value; + return this; + } + + public SaveAndFinishWorker setRequestWriteRepairModePassword(boolean value) { + mRequestWriteRepairModePassword = value; + return this; + } + + public SaveAndFinishWorker setBlocking(boolean blocking) { + mBlocking = blocking; + return this; + } + + public SaveAndFinishWorker setProfileToUnify( + int profileId, LockscreenCredential credential) { + mUnificationProfileId = profileId; + mUnificationProfileCredential = credential.duplicate(); + return this; + } + + private void unifyProfileCredentialIfRequested() { + if (mUnificationProfileId != UserHandle.USER_NULL) { + mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false, + mUnificationProfileCredential); + } + } + + private class Task extends AsyncTask> { + + @Override + protected Pair doInBackground(Void... params){ + return saveAndVerifyInBackground(); + } + + @Override + protected void onPostExecute(Pair resultData) { + if (!resultData.first) { + Toast.makeText(getContext(), R.string.lockpassword_credential_changed, + Toast.LENGTH_LONG).show(); + } + finish(resultData.second); + } + } + + interface Listener { + void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData); + } +} diff --git a/src/com/android/settings/password/SaveChosenLockWorkerBase.java b/src/com/android/settings/password/SaveChosenLockWorkerBase.java deleted file mode 100644 index 48649412a9c..00000000000 --- a/src/com/android/settings/password/SaveChosenLockWorkerBase.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.password; - -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.UserHandle; -import android.util.Pair; -import android.widget.Toast; - -import androidx.fragment.app.Fragment; - -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockscreenCredential; -import com.android.settings.R; -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 { - - 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 boolean mBlocking; - - @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 requestGatekeeperPassword, 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() { - if (mBlocking) { - finish(saveAndVerifyInBackground().second); - } else { - new Task().execute(); - } - } - - /** - * Executes the save and verify work in background. - * @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(); - - protected void finish(Intent resultData) { - mFinished = true; - mResultData = resultData; - if (mListener != null) { - mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData); - } - if (mUnificationProfileCredential != null) { - mUnificationProfileCredential.zeroize(); - } - LockScreenSafetySource.onLockScreenChange(getContext()); - } - - public void setBlocking(boolean blocking) { - mBlocking = blocking; - } - - public void setProfileToUnify(int profileId, LockscreenCredential credential) { - mUnificationProfileId = profileId; - mUnificationProfileCredential = credential.duplicate(); - } - - protected void unifyProfileCredentialIfRequested() { - if (mUnificationProfileId != UserHandle.USER_NULL) { - mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false, - mUnificationProfileCredential); - } - } - - private class Task extends AsyncTask> { - - @Override - protected Pair doInBackground(Void... params){ - return saveAndVerifyInBackground(); - } - - @Override - protected void onPostExecute(Pair resultData) { - if (!resultData.first) { - Toast.makeText(getContext(), R.string.lockpassword_credential_changed, - Toast.LENGTH_LONG).show(); - } - finish(resultData.second); - } - } - - interface Listener { - void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData); - } -} 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 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); + } +}