"Forgot my password" to start profile in locked state.

Currently if a work profile with a separate lock is turned off
(a.k.a. in quiet mode), and the user has forgotten the password,
profile owner app cannot use DPM.resetPasswordWithToken because
the profile user is not running.

In BYOD case the user can remove and re-provision the profile but
in the new COPE mode (a.k.a. on an organization owned device with
work profile) it is not possible to remove the profile. So full
factory reset is required.

This CL allows the user to start the profile in locked state
(a.k.a direct boot mode) so that the admin can reset the password.

This CL adds "Forgot my password" button to work profile credential prompt
if all of the following conditions are true:
 * Work profile is turned off
 * Profile owner app is capable of running in direct boot mode.
 * Profile owner app has an active password reset token.
 * The device is an FBE device (otherwise profile will be unlocked).

Clicking this button starts the profile in locked state and shows an
activity to the user that instruct them to go to their IT admin.

Bug: 143516540
Test: manual
Change-Id: I832f7121b43e39161c5afa816f44ce89584b66e2
This commit is contained in:
Pavel Grafov
2020-02-13 21:39:37 +00:00
parent f2a52c30a1
commit 04f783c759
12 changed files with 216 additions and 18 deletions

View File

@@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.view.View;
@@ -77,6 +78,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
protected boolean mReturnCredentials = false;
protected Button mCancelButton;
protected Button mForgotButton;
protected int mEffectiveUserId;
protected int mUserId;
protected UserManager mUserManager;
@@ -116,8 +118,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
mCancelButton = view.findViewById(R.id.cancelButton);
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
SHOW_CANCEL_BUTTON, false);
boolean hasAlternateButton = mFrp && !TextUtils.isEmpty(mFrpAlternateButtonText);
@@ -126,20 +127,27 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
if (hasAlternateButton) {
mCancelButton.setText(mFrpAlternateButtonText);
}
mCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (hasAlternateButton) {
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
}
getActivity().finish();
mCancelButton.setOnClickListener(v -> {
if (hasAlternateButton) {
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
}
getActivity().finish();
});
int credentialOwnerUserId = Utils.getCredentialOwnerUserId(
getActivity(),
Utils.getUserIdFromBundle(
getActivity(),
getActivity().getIntent().getExtras(), isInternalActivity()));
mForgotButton = view.findViewById(R.id.forgotButton);
if (mUserManager.isManagedProfile(mUserId)
&& mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))
&& mDevicePolicyManager.canProfileOwnerResetPasswordWhenLocked(mUserId)) {
mForgotButton.setVisibility(View.VISIBLE);
mForgotButton.setOnClickListener(v -> {
final Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME, ForgotPasswordActivity.class.getName());
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
getActivity().startActivity(intent);
getActivity().finish();
});
} else {
mForgotButton.setVisibility(View.GONE);
}
}
// User could be locked while Effective user is unlocked even though the effective owns the

View File

@@ -16,6 +16,7 @@
package com.android.settings.password;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -203,6 +204,16 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mForgotButton != null) {
mForgotButton.setText(mIsAlpha
? R.string.lockpassword_forgot_password
: R.string.lockpassword_forgot_pin);
}
}
private int getDefaultHeader() {
if (mFrp) {
return mIsAlpha ? R.string.lockpassword_confirm_your_password_header_frp
@@ -256,6 +267,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
mHeaderTextView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
mForgotButton.setAlpha(0f);
mPasswordEntry.setAlpha(0f);
mErrorTextView.setAlpha(0f);
}
@@ -267,6 +279,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(mCancelButton);
}
if (mForgotButton.getVisibility() == View.VISIBLE) {
result.add(mForgotButton);
}
result.add(mPasswordEntry);
result.add(mErrorTextView);
return result.toArray(new View[] {});

View File

@@ -16,6 +16,7 @@
package com.android.settings.password;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Intent;
@@ -181,9 +182,18 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
}
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mForgotButton != null) {
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
// deliberately not calling super since we are managing this in full
@@ -230,6 +240,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
super.prepareEnterAnimation();
mHeaderTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
mForgotButton.setAlpha(0f);
mLockPatternView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
}
@@ -252,10 +263,13 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
private Object[][] getActiveViews() {
ArrayList<ArrayList<Object>> result = new ArrayList<>();
result.add(new ArrayList<Object>(Collections.singletonList(mHeaderTextView)));
result.add(new ArrayList<Object>(Collections.singletonList(mDetailsTextView)));
result.add(new ArrayList<>(Collections.singletonList(mHeaderTextView)));
result.add(new ArrayList<>(Collections.singletonList(mDetailsTextView)));
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<Object>(Collections.singletonList(mCancelButton)));
result.add(new ArrayList<>(Collections.singletonList(mCancelButton)));
}
if (mForgotButton.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<>(Collections.singletonList(mForgotButton)));
}
LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
for (int i = 0; i < cellStates.length; i++) {

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2020 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.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.settings.R;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
/**
* An activity that asks the user to contact their admin to get assistance with forgotten password.
*/
public class ForgotPasswordActivity extends Activity {
public static final String TAG = ForgotPasswordActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int userId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, -1);
if (userId < 0) {
Log.e(TAG, "No valid userId supplied, exiting");
finish();
return;
}
setContentView(R.layout.forgot_password_activity);
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
layout.getMixin(FooterBarMixin.class).setPrimaryButton(
new FooterButton.Builder(this)
.setText(android.R.string.ok)
.setListener(v -> finish())
.setButtonType(FooterButton.ButtonType.DONE)
.setTheme(R.style.SudGlifButton_Primary)
.build()
);
UserManager.get(this).requestQuietModeEnabled(
false, UserHandle.of(userId), UserManager.QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL);
}
}