Add auth challenge for increasing screen timeout.
We only require one auth after onStart(), and only for increasing the timeout. Test: atest SettingsRoboTests:com.android.settings.display.ScreenTimeoutSettingsTest Test: also manually tested Bug: 315937886 Change-Id: If4aed67736cd7545d3a518aadd8253ea6a9fae43
This commit is contained in:
9
aconfig/settings_display_flag_declarations.aconfig
Normal file
9
aconfig/settings_display_flag_declarations.aconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
package: "com.android.settings.flags"
|
||||
|
||||
flag {
|
||||
name: "protect_screen_timeout_with_auth"
|
||||
namespace: "safety_center"
|
||||
description: "Require an auth challenge for increasing screen timeout."
|
||||
bug: "315937886"
|
||||
}
|
||||
|
@@ -37,10 +37,12 @@ import android.util.Log;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.support.actionbar.HelpResourceProvider;
|
||||
import com.android.settings.widget.RadioButtonPickerFragment;
|
||||
import com.android.settings.wifi.dpp.WifiDppUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
@@ -55,13 +57,12 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fragment that is used to control screen timeout.
|
||||
*/
|
||||
/** Fragment that is used to control screen timeout. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
HelpResourceProvider {
|
||||
public class ScreenTimeoutSettings extends RadioButtonPickerFragment
|
||||
implements HelpResourceProvider {
|
||||
private static final String TAG = "ScreenTimeout";
|
||||
|
||||
/** If there is no setting in the provider, use this. */
|
||||
public static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000;
|
||||
|
||||
@@ -72,7 +73,8 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
private FooterPreference mPrivacyPreference;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private SensorPrivacyManager mPrivacyManager;
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
private final BroadcastReceiver mReceiver =
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
|
||||
@@ -82,15 +84,13 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
private SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener;
|
||||
private boolean mIsUserAuthenticated = false;
|
||||
|
||||
@VisibleForTesting
|
||||
Context mContext;
|
||||
@VisibleForTesting Context mContext;
|
||||
|
||||
@VisibleForTesting
|
||||
RestrictedLockUtils.EnforcedAdmin mAdmin;
|
||||
@VisibleForTesting RestrictedLockUtils.EnforcedAdmin mAdmin;
|
||||
|
||||
@VisibleForTesting
|
||||
FooterPreference mDisableOptionsPreference;
|
||||
@VisibleForTesting FooterPreference mDisableOptionsPreference;
|
||||
|
||||
@VisibleForTesting
|
||||
FooterPreference mPowerConsumptionPreference;
|
||||
@@ -101,16 +101,14 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
@VisibleForTesting
|
||||
AdaptiveSleepCameraStatePreferenceController mAdaptiveSleepCameraStatePreferenceController;
|
||||
|
||||
@VisibleForTesting
|
||||
AdaptiveSleepPreferenceController mAdaptiveSleepController;
|
||||
@VisibleForTesting AdaptiveSleepPreferenceController mAdaptiveSleepController;
|
||||
|
||||
@VisibleForTesting
|
||||
AdaptiveSleepBatterySaverPreferenceController mAdaptiveSleepBatterySaverPreferenceController;
|
||||
|
||||
public ScreenTimeoutSettings() {
|
||||
super();
|
||||
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory()
|
||||
.getMetricsFeatureProvider();
|
||||
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,8 +119,8 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
mInitialEntries = getResources().getStringArray(R.array.screen_timeout_entries);
|
||||
mInitialValues = getResources().getStringArray(R.array.screen_timeout_values);
|
||||
mAdaptiveSleepController = new AdaptiveSleepPreferenceController(context);
|
||||
mAdaptiveSleepPermissionController = new AdaptiveSleepPermissionPreferenceController(
|
||||
context);
|
||||
mAdaptiveSleepPermissionController =
|
||||
new AdaptiveSleepPermissionPreferenceController(context);
|
||||
mAdaptiveSleepCameraStatePreferenceController =
|
||||
new AdaptiveSleepCameraStatePreferenceController(context, getLifecycle());
|
||||
mAdaptiveSleepBatterySaverPreferenceController =
|
||||
@@ -144,8 +142,9 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
if (mInitialValues != null) {
|
||||
for (int i = 0; i < mInitialValues.length; ++i) {
|
||||
if (Long.parseLong(mInitialValues[i].toString()) <= maxTimeout) {
|
||||
candidates.add(new TimeoutCandidateInfo(mInitialEntries[i],
|
||||
mInitialValues[i].toString(), true));
|
||||
candidates.add(
|
||||
new TimeoutCandidateInfo(
|
||||
mInitialEntries[i], mInitialValues[i].toString(), true));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -161,9 +160,10 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
mAdaptiveSleepCameraStatePreferenceController.updateVisibility();
|
||||
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
|
||||
mAdaptiveSleepController.updatePreference();
|
||||
mContext.registerReceiver(mReceiver,
|
||||
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
|
||||
mContext.registerReceiver(
|
||||
mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
|
||||
mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener);
|
||||
mIsUserAuthenticated = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,19 +185,21 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
}
|
||||
|
||||
for (CandidateInfo info : candidateList) {
|
||||
SelectorWithWidgetPreference pref =
|
||||
new SelectorWithWidgetPreference(getPrefContext());
|
||||
ProtectedSelectorWithWidgetPreference pref =
|
||||
new ProtectedSelectorWithWidgetPreference(
|
||||
getPrefContext(), info.getKey(), this);
|
||||
bindPreference(pref, info.getKey(), info, defaultKey);
|
||||
screen.addPreference(pref);
|
||||
}
|
||||
|
||||
final long selectedTimeout = Long.parseLong(defaultKey);
|
||||
final long selectedTimeout = getTimeoutFromKey(defaultKey);
|
||||
final long maxTimeout = getMaxScreenTimeout(getContext());
|
||||
if (!candidateList.isEmpty() && (selectedTimeout > maxTimeout)) {
|
||||
// The selected time out value is longer than the max timeout allowed by the admin.
|
||||
// Select the largest value from the list by default.
|
||||
final SelectorWithWidgetPreference preferenceWithLargestTimeout =
|
||||
(SelectorWithWidgetPreference) screen.getPreference(candidateList.size() - 1);
|
||||
final ProtectedSelectorWithWidgetPreference preferenceWithLargestTimeout =
|
||||
(ProtectedSelectorWithWidgetPreference)
|
||||
screen.getPreference(candidateList.size() - 1);
|
||||
preferenceWithLargestTimeout.setChecked(true);
|
||||
}
|
||||
|
||||
@@ -225,18 +227,32 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
}
|
||||
}
|
||||
|
||||
boolean isUserAuthenticated() {
|
||||
return mIsUserAuthenticated;
|
||||
}
|
||||
|
||||
void setUserAuthenticated(boolean isUserAuthenticated) {
|
||||
mIsUserAuthenticated = isUserAuthenticated;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setupDisabledFooterPreference() {
|
||||
final String textDisabledByAdmin = mDevicePolicyManager.getResources().getString(
|
||||
OTHER_OPTIONS_DISABLED_BY_ADMIN, () -> getResources().getString(
|
||||
R.string.admin_disabled_other_options));
|
||||
final String textDisabledByAdmin =
|
||||
mDevicePolicyManager
|
||||
.getResources()
|
||||
.getString(
|
||||
OTHER_OPTIONS_DISABLED_BY_ADMIN,
|
||||
() ->
|
||||
getResources()
|
||||
.getString(R.string.admin_disabled_other_options));
|
||||
final String textMoreDetails = getResources().getString(R.string.admin_more_details);
|
||||
|
||||
mDisableOptionsPreference = new FooterPreference(getContext());
|
||||
mDisableOptionsPreference.setTitle(textDisabledByAdmin);
|
||||
mDisableOptionsPreference.setSelectable(false);
|
||||
mDisableOptionsPreference.setLearnMoreText(textMoreDetails);
|
||||
mDisableOptionsPreference.setLearnMoreAction(v -> {
|
||||
mDisableOptionsPreference.setLearnMoreAction(
|
||||
v -> {
|
||||
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), mAdmin);
|
||||
});
|
||||
mDisableOptionsPreference.setIcon(R.drawable.ic_info_outline_24dp);
|
||||
@@ -303,17 +319,20 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
if (context == null) {
|
||||
return Long.toString(FALLBACK_SCREEN_TIMEOUT_VALUE);
|
||||
} else {
|
||||
return Long.toString(Settings.System.getLong(context.getContentResolver(),
|
||||
SCREEN_OFF_TIMEOUT, FALLBACK_SCREEN_TIMEOUT_VALUE));
|
||||
return Long.toString(
|
||||
Settings.System.getLong(
|
||||
context.getContentResolver(),
|
||||
SCREEN_OFF_TIMEOUT,
|
||||
FALLBACK_SCREEN_TIMEOUT_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentSystemScreenTimeout(Context context, String key) {
|
||||
try {
|
||||
if (context != null) {
|
||||
final long value = Long.parseLong(key);
|
||||
mMetricsFeatureProvider.action(context, SettingsEnums.ACTION_SCREEN_TIMEOUT_CHANGED,
|
||||
(int) value);
|
||||
final long value = getTimeoutFromKey(key);
|
||||
mMetricsFeatureProvider.action(
|
||||
context, SettingsEnums.ACTION_SCREEN_TIMEOUT_CHANGED, (int) value);
|
||||
Settings.System.putLong(context.getContentResolver(), SCREEN_OFF_TIMEOUT, value);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
@@ -325,7 +344,12 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
return AdaptiveSleepPreferenceController.isAdaptiveSleepSupported(context);
|
||||
}
|
||||
|
||||
private static class TimeoutCandidateInfo extends CandidateInfo {
|
||||
private static long getTimeoutFromKey(String key) {
|
||||
return Long.parseLong(key);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class TimeoutCandidateInfo extends CandidateInfo {
|
||||
private final CharSequence mLabel;
|
||||
private final String mKey;
|
||||
|
||||
@@ -351,10 +375,42 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class ProtectedSelectorWithWidgetPreference
|
||||
extends SelectorWithWidgetPreference {
|
||||
|
||||
private final long mTimeoutMs;
|
||||
private final ScreenTimeoutSettings mScreenTimeoutSettings;
|
||||
|
||||
ProtectedSelectorWithWidgetPreference(
|
||||
Context context, String key, ScreenTimeoutSettings screenTimeoutSettings) {
|
||||
super(context);
|
||||
mTimeoutMs = getTimeoutFromKey(key);
|
||||
mScreenTimeoutSettings = screenTimeoutSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
if (Flags.protectScreenTimeoutWithAuth()
|
||||
&& !mScreenTimeoutSettings.isUserAuthenticated()
|
||||
&& !isChecked()
|
||||
&& mTimeoutMs > getTimeoutFromKey(mScreenTimeoutSettings.getDefaultKey())) {
|
||||
WifiDppUtils.showLockScreen(
|
||||
getContext(),
|
||||
() -> {
|
||||
mScreenTimeoutSettings.setUserAuthenticated(true);
|
||||
super.onClick();
|
||||
});
|
||||
} else {
|
||||
super.onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.screen_timeout_settings) {
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
|
||||
boolean enabled) {
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(
|
||||
Context context, boolean enabled) {
|
||||
if (!isScreenAttentionAvailable(context)) {
|
||||
return null;
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -41,31 +42,44 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.widget.CandidateInfo;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowKeyguardManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
ShadowKeyguardManager.class
|
||||
})
|
||||
public class ScreenTimeoutSettingsTest {
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
private static final String[] TIMEOUT_ENTRIES = new String[]{"15 secs", "30 secs"};
|
||||
private static final String[] TIMEOUT_VALUES = new String[]{"15000", "30000"};
|
||||
|
||||
@@ -218,4 +232,85 @@ public class ScreenTimeoutSettingsTest {
|
||||
|
||||
assertThat(Long.toString(timeout)).isEqualTo(TIMEOUT_VALUES[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_PROTECT_SCREEN_TIMEOUT_WITH_AUTH)
|
||||
public void onClick_whenUserAlreadyAuthenticated_buttonChecked() {
|
||||
String key = "222";
|
||||
String defaultKey = "1";
|
||||
mSettings.setDefaultKey(defaultKey);
|
||||
CandidateInfo info = new ScreenTimeoutSettings.TimeoutCandidateInfo("label", key, false);
|
||||
ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference pref =
|
||||
new ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference(
|
||||
mContext, info.getKey(), mSettings);
|
||||
mSettings.bindPreference(pref, info.getKey(), info, defaultKey);
|
||||
mSettings.setUserAuthenticated(true);
|
||||
|
||||
pref.onClick();
|
||||
|
||||
assertThat(mSettings.getDefaultKey()).isEqualTo(key);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_PROTECT_SCREEN_TIMEOUT_WITH_AUTH)
|
||||
public void onClick_whenButtonAlreadyChecked_noAuthNeeded() {
|
||||
String key = "222";
|
||||
mSettings.setDefaultKey(key);
|
||||
CandidateInfo info = new ScreenTimeoutSettings.TimeoutCandidateInfo("label", key, false);
|
||||
ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference pref =
|
||||
new ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference(
|
||||
mContext, info.getKey(), mSettings);
|
||||
mSettings.bindPreference(pref, info.getKey(), info, key);
|
||||
mSettings.setUserAuthenticated(false);
|
||||
setAuthPassesAutomatically();
|
||||
|
||||
pref.onClick();
|
||||
|
||||
assertThat(mSettings.isUserAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_PROTECT_SCREEN_TIMEOUT_WITH_AUTH)
|
||||
public void onClick_whenReducingTimeout_noAuthNeeded() {
|
||||
String key = "1";
|
||||
String defaultKey = "222";
|
||||
mSettings.setDefaultKey(defaultKey);
|
||||
CandidateInfo info = new ScreenTimeoutSettings.TimeoutCandidateInfo("label", key, false);
|
||||
ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference pref =
|
||||
new ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference(
|
||||
mContext, info.getKey(), mSettings);
|
||||
mSettings.bindPreference(pref, info.getKey(), info, defaultKey);
|
||||
mSettings.setUserAuthenticated(false);
|
||||
setAuthPassesAutomatically();
|
||||
|
||||
pref.onClick();
|
||||
|
||||
assertThat(mSettings.isUserAuthenticated()).isFalse();
|
||||
assertThat(mSettings.getDefaultKey()).isEqualTo(key);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_PROTECT_SCREEN_TIMEOUT_WITH_AUTH)
|
||||
public void onClick_whenIncreasingTimeout_authNeeded() {
|
||||
String key = "222";
|
||||
String defaultKey = "1";
|
||||
mSettings.setDefaultKey(defaultKey);
|
||||
CandidateInfo info = new ScreenTimeoutSettings.TimeoutCandidateInfo("label", key, false);
|
||||
ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference pref =
|
||||
new ScreenTimeoutSettings.ProtectedSelectorWithWidgetPreference(
|
||||
mContext, info.getKey(), mSettings);
|
||||
mSettings.bindPreference(pref, info.getKey(), info, defaultKey);
|
||||
mSettings.setUserAuthenticated(false);
|
||||
setAuthPassesAutomatically();
|
||||
|
||||
pref.onClick();
|
||||
|
||||
assertThat(mSettings.getDefaultKey()).isEqualTo(key);
|
||||
assertThat(mSettings.isUserAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
private void setAuthPassesAutomatically() {
|
||||
Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class))
|
||||
.setIsKeyguardSecure(false);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user