Prompt user to Setup a device lock if not set

This add below changes on selecting private space setting:
1. Prompt user to setup device lock if not already set
2. If device lock is set authenticate user first before displaying private space settings page

Screenshot - https://screenshot.googleplex.com/4SrYHbBMJfVuoRy.png
https://screenshot.googleplex.com/6vNWm7Lg83vfnH8.png
RecordingLink - https://drive.google.com/file/d/1r4zb3ILPRqwvP5tlwfjQ9GgnDAW4vZg6/view?usp=drive_link

Bug: 289016927
Test: atest PrivateSpaceSettingsAuthenticatorTest , atest SecuritySettingsTest
Change-Id: I0e5dfb30213843c0dec60a17d01c30cd91db89b0
This commit is contained in:
josephpv
2023-10-04 18:14:20 +00:00
parent 852ec4e22d
commit 31b044ed6a
10 changed files with 416 additions and 15 deletions

View File

@@ -0,0 +1,152 @@
/*
* 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.privatespace;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Flags;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.transition.SettingsTransitionHelper;
import com.google.android.setupdesign.util.ThemeHelper;
/**
* Prompts user to set a device lock if not set with an alert dialog.
* If a lock is already set then first authenticates user before displaying private space settings
* page.
*/
public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
private static final String TAG = "PrivateSpaceAuthCheck";
private PrivateSpaceMaintainer mPrivateSpaceMaintainer;
private KeyguardManager mKeyguardManager;
private final ActivityResultLauncher<Intent> mSetDeviceLock =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
this::onSetDeviceLockResult);
private final ActivityResultLauncher<Intent> mVerifyDeviceLock =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
this::onVerifyDeviceLock);
static class Injector {
PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) {
return PrivateSpaceMaintainer.getInstance(context);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Flags.allowPrivateProfile()) {
ThemeHelper.trySetDynamicColor(this);
mPrivateSpaceMaintainer = new Injector().injectPrivateSpaceMaintainer(
getApplicationContext());
if (getKeyguardManager().isDeviceSecure()) {
if (savedInstanceState == null) {
Intent credentialIntent =
mPrivateSpaceMaintainer.getPrivateProfileLockCredentialIntent();
if (credentialIntent != null) {
mVerifyDeviceLock.launch(credentialIntent);
} else {
Log.e(TAG, "verifyCredentialIntent is null even though device lock is set");
finish();
}
}
} else {
promptToSetDeviceLock();
}
} else {
Log.w(TAG, "allowPrivateProfile flag is Off!");
finish();
}
}
/** Show private space settings page on device lock authentications */
@VisibleForTesting
public void onLockAuthentication(Context context) {
new SubSettingLauncher(context)
.setDestination(PrivateSpaceDashboardFragment.class.getName())
.setTransitionType(
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
.setSourceMetricsCategory(SettingsEnums.PRIVATE_SPACE_SETTINGS)
.launch();
}
@VisibleForTesting
public void setPrivateSpaceMaintainer(Injector injector) {
mPrivateSpaceMaintainer = injector.injectPrivateSpaceMaintainer(getApplicationContext());
}
private void promptToSetDeviceLock() {
new AlertDialog.Builder(this)
.setTitle(R.string.no_device_lock_title)
.setMessage(R.string.no_device_lock_summary)
.setPositiveButton(
R.string.no_device_lock_action_label,
(DialogInterface dialog, int which) -> {
mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD));
})
.setNegativeButton(
R.string.no_device_lock_cancel,
(DialogInterface dialog, int which) -> finish())
.setOnCancelListener(
(DialogInterface dialog) -> {
finish();
})
.show();
}
private KeyguardManager getKeyguardManager() {
if (mKeyguardManager == null) {
mKeyguardManager = getSystemService(KeyguardManager.class);
}
return mKeyguardManager;
}
private void onSetDeviceLockResult(@Nullable ActivityResult result) {
if (result != null) {
if (getKeyguardManager().isDeviceSecure()) {
onLockAuthentication(this);
}
finish();
}
}
private void onVerifyDeviceLock(@Nullable ActivityResult result) {
if (result != null && result.getResultCode() == RESULT_OK) {
onLockAuthentication(this);
}
finish();
}
}

View File

@@ -20,7 +20,9 @@ import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -28,6 +30,8 @@ import android.os.UserManager;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
import java.util.List;
@@ -43,6 +47,7 @@ public class PrivateSpaceMaintainer {
private final UserManager mUserManager;
@GuardedBy("this")
private UserHandle mUserHandle;
private final KeyguardManager mKeyguardManager;
public enum ErrorDeletingPrivateSpace {
DELETE_PS_ERROR_NONE,
@@ -140,6 +145,23 @@ public class PrivateSpaceMaintainer {
return mUserManager.isQuietModeEnabled(mUserHandle);
}
/**
* Returns an intent to prompt the user to confirm private profile credentials if it is set
* otherwise returns intent to confirm device credentials.
*/
@Nullable
public synchronized Intent getPrivateProfileLockCredentialIntent() {
//TODO(b/307281644): To replace with check for doesPrivateSpaceExist() method once Auth
// changes are merged.
if (isPrivateProfileLockSet()) {
return mKeyguardManager.createConfirmDeviceCredentialIntent(
/* title= */ null, /* description= */null, mUserHandle.getIdentifier());
}
// TODO(b/304796434) Need to try changing this intent to use BiometricPrompt
return mKeyguardManager.createConfirmDeviceCredentialIntent(
/* title= */ null, /* description= */ null);
}
/** Returns the instance of {@link PrivateSpaceMaintainer} */
public static synchronized PrivateSpaceMaintainer getInstance(Context context) {
if (sPrivateSpaceMaintainer == null) {
@@ -151,5 +173,19 @@ public class PrivateSpaceMaintainer {
private PrivateSpaceMaintainer(Context context) {
mContext = context.getApplicationContext();
mUserManager = mContext.getSystemService(UserManager.class);
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
}
// TODO(b/307281644): Remove this method once new auth change is merged
/**
* Returns true if private space exists and a separate private profile lock is set
* otherwise false when the private space does not exit or exists but does not have a
* separate profile lock.
*/
@GuardedBy("this")
private boolean isPrivateProfileLockSet() {
return doesPrivateSpaceExist()
&& mKeyguardManager.isDeviceSecure(mUserHandle.getIdentifier());
}
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.privatespace;
import android.app.PendingIntent;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Flags;
@@ -28,9 +27,7 @@ import android.safetycenter.SafetySourceStatus;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settingslib.transition.SettingsTransitionHelper;
/** Private Space safety source for the Safety Center */
public final class PrivateSpaceSafetySource {
@@ -86,18 +83,15 @@ public final class PrivateSpaceSafetySource {
}
private static PendingIntent getPendingIntentForPsDashboard(Context context) {
Intent privateSpaceDashboardIntent = new SubSettingLauncher(context)
.setDestination(PrivateSpaceDashboardFragment.class.getName())
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
.setSourceMetricsCategory(SettingsEnums.PRIVATE_SPACE_SETTINGS)
.toIntent()
.setIdentifier(SAFETY_SOURCE_ID);
Intent privateSpaceAuthenticationIntent =
new Intent(context, PrivateSpaceAuthenticationActivity.class)
.setIdentifier(SAFETY_SOURCE_ID);
return PendingIntent
.getActivity(
context,
/* requestCode */ 0,
privateSpaceDashboardIntent,
privateSpaceAuthenticationIntent,
PendingIntent.FLAG_IMMUTABLE);
}
}