Merge "Remove a window flag."
diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml
index 69b00fe..4096925 100644
--- a/res/layout/face_enroll_introduction.xml
+++ b/res/layout/face_enroll_introduction.xml
@@ -85,12 +85,6 @@
android:layout_height="wrap_content"
FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
- <com.android.settings.biometrics.face.FaceEnrollAccessibilityToggle
- android:id="@+id/toggle_vision"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_vision"/>
-
</LinearLayout>
</FrameLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f77c10d..a4ecd3a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1430,6 +1430,16 @@
<!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]-->
<string name="unlock_change_lock_password_title">Change unlock password</string>
+ <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a strong PIN or password [CHAR LIMIT=NONE] -->
+ <string name="unlock_footer_high_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a strong PIN or password.</string>
+ <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a medium strength PIN or password [CHAR LIMIT=NONE] -->
+ <string name="unlock_footer_medium_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new PIN or password.</string>
+ <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and it requests for any screen lock [CHAR LIMIT=NONE] -->
+ <string name="unlock_footer_low_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new pattern, PIN or password.</string>
+ <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock [CHAR LIMIT=NONE] -->
+ <string name="unlock_footer_none_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new screen lock.</string>
+
+
<!-- Message shown on the lock screen when the user incorrectly enters their lock and it counts towards the max attempts before their data on the device is wiped. [CHAR LIMIT=NONE] -->
<string name="lock_failed_attempts_before_wipe">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string>
@@ -10363,6 +10373,17 @@
<string name="mobile_data_settings_title">Mobile data</string>
<!-- Mobile network settings screen, title of Mobile data switch preference [CHAR LIMIT=NONE] -->
<string name="mobile_data_settings_summary">Access data using mobile network</string>
+ <!-- Mobile network settings screen, title of item showing the name of the default subscription
+ that will be used for calls. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] -->
+ <string name="calls_preference">Calls preference</string>
+ <!-- Mobile network settings screen, title of item showing the name of the default subscription
+ that will be used for SMS messages. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] -->
+ <string name="sms_preference">SMS preference</string>
+ <!-- Mobile network settings screen, a label in a chooser dialog that appears when choosing the
+ default subscription to use for either calls or SMS when in multi-SIM mode. This label means
+ that the user will be asked which mobile network subscription to use every time they place a
+ call or send an SMS, instead of defaulting to one particular subscription. [CHAR LIMIT=40]-->
+ <string name="calls_and_sms_ask_every_time">Ask every time</string>
<!-- Summary of the 'Mobile network' item on the Network & internet page when there is no mobile
service setup yet (eg no SIM card inserted and no eSIM configured). Tapping it leads to a
diff --git a/res/xml/mobile_network_settings_v2.xml b/res/xml/mobile_network_settings_v2.xml
index 7a19c32..6273ad9 100644
--- a/res/xml/mobile_network_settings_v2.xml
+++ b/res/xml/mobile_network_settings_v2.xml
@@ -17,24 +17,37 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="mobile_network_pref_screen"
- settings:initialExpandedChildrenCount="5">
+ settings:initialExpandedChildrenCount="7">
<com.android.settings.datausage.DataUsageSummaryPreference
android:key="status_header"
android:visibility="gone"
android:selectable="false" />
+ <ListPreference
+ android:key="calls_preference"
+ android:title="@string/calls_preference"
+ settings:controller="com.android.settings.network.telephony.CallsDefaultSubscriptionController"
+ settings:allowDividerAbove="true" />
+
+ <ListPreference
+ android:key="sms_preference"
+ android:title="@string/sms_preference"
+ settings:controller="com.android.settings.network.telephony.SmsDefaultSubscriptionController" />
+
<Preference
android:key="cdma_lte_data_service_key"
android:title="@string/cdma_lte_data_service"
- settings:controller="com.android.settings.network.telephony.DataServiceSetupPreferenceController">
- </Preference>
+ settings:controller="com.android.settings.network.telephony.DataServiceSetupPreferenceController"
+ settings:allowDividerAbove="true"
+ settings:allowDividerBelow="false" />
<SwitchPreference
android:key="mobile_data_enable"
android:title="@string/mobile_data_settings_title"
android:summary="@string/mobile_data_settings_summary"
- settings:controller="com.android.settings.network.telephony.MobileDataPreferenceController"/>
+ settings:controller="com.android.settings.network.telephony.MobileDataPreferenceController"
+ settings:allowDividerAbove="true"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="button_roaming_key"
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 5ab543f..0485a0f 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -55,40 +55,7 @@
import sun.security.x509.AlgorithmId;
/**
- * CredentialStorage handles KeyStore reset, unlock, and install.
- *
- * CredentialStorage has a pretty convoluted state machine to migrate
- * from the old style separate keystore password to a new key guard
- * based password, as well as to deal with setting up the key guard if
- * necessary.
- *
- * KeyStore: UNINITALIZED
- * KeyGuard: OFF
- * Action: set up key guard
- * Notes: factory state
- *
- * KeyStore: UNINITALIZED
- * KeyGuard: ON
- * Action: confirm key guard
- * Notes: user had key guard but no keystore and upgraded from pre-ICS
- * OR user had key guard and pre-ICS keystore password which was then reset
- *
- * KeyStore: LOCKED
- * KeyGuard: OFF/ON
- * Action: confirm key guard
- * Notes: request normal unlock to unlock the keystore.
- * if unlock, ensure key guard before install.
- * if reset, treat as UNINITALIZED/OFF
- *
- * KeyStore: UNLOCKED
- * KeyGuard: OFF
- * Action: set up key guard
- * Notes: ensure key guard, then proceed
- *
- * KeyStore: UNLOCKED
- * keyguard: ON
- * Action: normal unlock/install
- * Notes: this is the common case
+ * CredentialStorage handles resetting and installing keys into KeyStore.
*/
public final class CredentialStorage extends FragmentActivity {
@@ -102,8 +69,7 @@
// lower than this, keystore should not be activated.
public static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- private static final int CONFIRM_KEY_GUARD_REQUEST = 1;
- private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 2;
+ private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;
private final KeyStore mKeyStore = KeyStore.getInstance();
private LockPatternUtils mUtils;
@@ -133,75 +99,26 @@
if (ACTION_INSTALL.equals(action) && checkCallerIsCertInstallerOrSelfInProfile()) {
mInstallBundle = intent.getExtras();
}
- // ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
- handleUnlockOrInstall();
+ handleInstall();
}
} else {
- // Users can set a screen lock if there is none even if they can't modify the
- // credentials store.
- if (ACTION_UNLOCK.equals(action) && mKeyStore.state() == KeyStore.State.UNINITIALIZED) {
- ensureKeyGuard();
- } else {
- finish();
- }
+ finish();
}
}
/**
- * Based on the current state of the KeyStore and key guard, try to
- * make progress on unlocking or installing to the keystore.
+ * Install credentials from mInstallBundle into Keystore.
*/
- private void handleUnlockOrInstall() {
+ private void handleInstall() {
// something already decided we are done, do not proceed
if (isFinishing()) {
return;
}
- switch (mKeyStore.state()) {
- case UNINITIALIZED: {
- ensureKeyGuard();
- return;
- }
- case LOCKED: {
- // Force key guard confirmation
- confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST);
- return;
- }
- case UNLOCKED: {
- if (!mUtils.isSecure(UserHandle.myUserId())) {
- final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
- dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
- return;
- }
- if (installIfAvailable()) {
- finish();
- }
- return;
- }
+ if (installIfAvailable()) {
+ finish();
}
}
- /**
- * Make sure the user enters the key guard to set or change the
- * keystore password. This can be used in UNINITIALIZED to set the
- * keystore password or UNLOCKED to change the password (as is the
- * case after unlocking with an old-style password).
- */
- private void ensureKeyGuard() {
- if (!mUtils.isSecure(UserHandle.myUserId())) {
- // key guard not setup, doing so will initialize keystore
- final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
- dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
- // will return to onResume after Activity
- return;
- }
- // force key guard confirmation
- if (confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST)) {
- // will return password value via onActivityResult
- return;
- }
- finish();
- }
-
private boolean isHardwareBackedKey(byte[] keyData) {
try {
final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
@@ -254,15 +171,7 @@
final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
- int flags = KeyStore.FLAG_ENCRYPTED;
- if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) {
- // Hardware backed keystore is secure enough to allow for WIFI stack
- // to enable access to secure networks without user intervention
- Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID");
- flags = KeyStore.FLAG_NONE;
- }
-
- if (!mKeyStore.importKey(key, value, uid, flags)) {
+ if (!mKeyStore.importKey(key, value, uid, KeyStore.FLAG_NONE)) {
Log.e(TAG, "Failed to install " + key + " as uid " + uid);
return true;
}
@@ -475,20 +384,7 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- // Receive key guard password initiated by confirmKeyGuard.
- if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
- if (resultCode == Activity.RESULT_OK) {
- final String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
- if (!TextUtils.isEmpty(password)) {
- // success
- mKeyStore.unlock(password);
- // return to onResume
- return;
- }
- }
- // failed confirmation, bail
- finish();
- } else if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
+ if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
new ResetKeyStoreAndKeyChain().execute();
return;
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index fc4f6ce..71bd02f 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -43,7 +43,6 @@
private static final String TAG = "FaceIntro";
private FaceManager mFaceManager;
- private FaceEnrollAccessibilityToggle mSwitchVision;
private FaceEnrollAccessibilityToggle mSwitchDiversity;
@Override
@@ -57,7 +56,6 @@
accessibilityLayout.setVisibility(View.VISIBLE);
});
- mSwitchVision = findViewById(R.id.toggle_vision);
mSwitchDiversity = findViewById(R.id.toggle_diversity);
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
@@ -167,7 +165,6 @@
} else {
intent.setClass(this, FaceEnrollEnrolling.class);
}
- intent.putExtra(EXTRA_KEY_REQUIRE_VISION, mSwitchVision.isChecked());
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, mSwitchDiversity.isChecked());
return intent;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index c3d49e9..6ff4309 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -109,12 +109,14 @@
mCachedDevice = getCachedDevice(mDeviceAddress);
super.onAttach(context);
- if (FeatureFlagUtils.isEnabled(context, FeatureFlags.SLICE_INJECTION)) {
- final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(context)
- .getBluetoothFeatureProvider(context);
- use(BlockingSlicePrefController.class).setSliceUri(
- featureProvider.getBluetoothDeviceSettingsUri(mDeviceAddress));
- }
+ final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(
+ context).getBluetoothFeatureProvider(context);
+ final boolean injectionEnabled = FeatureFlagUtils.isEnabled(context,
+ FeatureFlags.SLICE_INJECTION);
+
+ use(BlockingSlicePrefController.class).setSliceUri(injectionEnabled
+ ? featureProvider.getBluetoothDeviceSettingsUri(mDeviceAddress)
+ : null);
}
@Override
diff --git a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
index 35cb5ed..17c16e7 100644
--- a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
@@ -67,8 +67,7 @@
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- final LayoutPreference preference = (LayoutPreference) screen.findPreference(
- getPreferenceKey());
+ final LayoutPreference preference = screen.findPreference(getPreferenceKey());
mTurnOnButton = preference.findViewById(R.id.night_display_turn_on_button);
mTurnOnButton.setOnClickListener(mListener);
mTurnOffButton = preference.findViewById(R.id.night_display_turn_off_button);
@@ -106,14 +105,14 @@
final int autoMode = mController.getAutoMode();
String buttonText;
- if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
+ if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME) {
buttonText = mContext.getString(isActivated
? R.string.night_display_activation_off_custom
: R.string.night_display_activation_on_custom,
mTimeFormatter.getFormattedTimeString(isActivated
? mController.getCustomStartTime()
: mController.getCustomEndTime()));
- } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
+ } else if (autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
buttonText = mContext.getString(isActivated
? R.string.night_display_activation_off_twilight
: R.string.night_display_activation_on_twilight);
diff --git a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
index 1710f51..33e3e6f 100644
--- a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
@@ -23,7 +23,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.internal.app.ColorDisplayController;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -31,11 +30,11 @@
implements Preference.OnPreferenceChangeListener {
private DropDownPreference mPreference;
- private ColorDisplayController mController;
+ private ColorDisplayManager mManager;
public NightDisplayAutoModePreferenceController(Context context, String key) {
super(context, key);
- mController = new ColorDisplayController(context);
+ mManager = context.getSystemService(ColorDisplayManager.class);
}
@Override
@@ -48,7 +47,7 @@
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- mPreference = (DropDownPreference) screen.findPreference(getPreferenceKey());
+ mPreference = screen.findPreference(getPreferenceKey());
mPreference.setEntries(new CharSequence[]{
mContext.getString(R.string.night_display_auto_mode_never),
@@ -56,19 +55,19 @@
mContext.getString(R.string.night_display_auto_mode_twilight)
});
mPreference.setEntryValues(new CharSequence[]{
- String.valueOf(ColorDisplayController.AUTO_MODE_DISABLED),
- String.valueOf(ColorDisplayController.AUTO_MODE_CUSTOM),
- String.valueOf(ColorDisplayController.AUTO_MODE_TWILIGHT)
+ String.valueOf(ColorDisplayManager.AUTO_MODE_DISABLED),
+ String.valueOf(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME),
+ String.valueOf(ColorDisplayManager.AUTO_MODE_TWILIGHT)
});
}
@Override
public final void updateState(Preference preference) {
- mPreference.setValue(String.valueOf(mController.getAutoMode()));
+ mPreference.setValue(String.valueOf(mManager.getNightDisplayAutoMode()));
}
@Override
public final boolean onPreferenceChange(Preference preference, Object newValue) {
- return mController.setAutoMode(Integer.parseInt((String) newValue));
+ return mManager.setNightDisplayAutoMode(Integer.parseInt((String) newValue));
}
}
diff --git a/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java b/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
index 2fa0ef5..b12c18a 100644
--- a/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayCustomEndTimePreferenceController.java
@@ -18,9 +18,7 @@
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
-
import androidx.preference.Preference;
-
import com.android.internal.app.ColorDisplayController;
import com.android.settings.core.BasePreferenceController;
@@ -44,7 +42,8 @@
@Override
public final void updateState(Preference preference) {
- preference.setVisible(mController.getAutoMode() == ColorDisplayController.AUTO_MODE_CUSTOM);
+ preference
+ .setVisible(mController.getAutoMode() == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
preference.setSummary(mTimeFormatter.getFormattedTimeString(
mController.getCustomEndTime()));
}
diff --git a/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java b/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
index cf740fd..10fb3a1 100644
--- a/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayCustomStartTimePreferenceController.java
@@ -18,9 +18,7 @@
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
-
import androidx.preference.Preference;
-
import com.android.internal.app.ColorDisplayController;
import com.android.settings.core.BasePreferenceController;
@@ -44,7 +42,8 @@
@Override
public final void updateState(Preference preference) {
- preference.setVisible(mController.getAutoMode() == ColorDisplayController.AUTO_MODE_CUSTOM);
+ preference
+ .setVisible(mController.getAutoMode() == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
preference.setSummary(mTimeFormatter.getFormattedTimeString(
mController.getCustomStartTime()));
}
diff --git a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
index 6adaf23..7487873 100644
--- a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
@@ -22,6 +22,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
import com.android.settings.core.SliderPreferenceController;
@@ -54,8 +55,7 @@
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- final SeekBarPreference preference = (SeekBarPreference) screen.findPreference(
- getPreferenceKey());
+ final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
preference.setContinuousUpdates(true);
preference.setMax(getMaxSteps());
}
diff --git a/src/com/android/settings/display/NightDisplayPreferenceController.java b/src/com/android/settings/display/NightDisplayPreferenceController.java
index 162a648..ab0250d 100644
--- a/src/com/android/settings/display/NightDisplayPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayPreferenceController.java
@@ -38,7 +38,7 @@
return true;
}
final ColorDisplayController controller = new ColorDisplayController(context);
- return controller.getAutoMode() != ColorDisplayController.AUTO_MODE_DISABLED;
+ return controller.getAutoMode() != ColorDisplayManager.AUTO_MODE_DISABLED;
}
@Override
diff --git a/src/com/android/settings/display/NightDisplayTimeFormatter.java b/src/com/android/settings/display/NightDisplayTimeFormatter.java
index 48a1994..1b82e0a 100644
--- a/src/com/android/settings/display/NightDisplayTimeFormatter.java
+++ b/src/com/android/settings/display/NightDisplayTimeFormatter.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
import com.android.internal.app.ColorDisplayController;
import com.android.settings.R;
@@ -54,7 +55,7 @@
private String getAutoModeSummary(Context context, ColorDisplayController controller) {
final boolean isActivated = controller.isActivated();
final int autoMode = controller.getAutoMode();
- if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
+ if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME) {
if (isActivated) {
return context.getString(R.string.night_display_summary_on_auto_mode_custom,
getFormattedTimeString(controller.getCustomEndTime()));
@@ -62,7 +63,7 @@
return context.getString(R.string.night_display_summary_off_auto_mode_custom,
getFormattedTimeString(controller.getCustomStartTime()));
}
- } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
+ } else if (autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
return context.getString(isActivated
? R.string.night_display_summary_on_auto_mode_twilight
: R.string.night_display_summary_off_auto_mode_twilight);
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
index ff26888..1602f56 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
@@ -30,16 +30,11 @@
private static final String TAG = "BluetoothUpdateWorker";
- private final Context mContext;
- private final Uri mUri;
private final LocalBluetoothManager mLocalBluetoothManager;
public BluetoothUpdateWorker(Context context, Uri uri) {
super(context, uri);
-
- mContext = context;
- mUri = uri;
- mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+ mLocalBluetoothManager = Utils.getLocalBtManager(context);
}
@Override
@@ -89,8 +84,4 @@
int bluetoothProfile) {
notifySliceChange();
}
-
- private void notifySliceChange() {
- mContext.getContentResolver().notifyChange(mUri, null);
- }
}
\ No newline at end of file
diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 2d84e21..3adc489 100644
--- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
@@ -13,6 +13,8 @@
*/
package com.android.settings.location;
+import static java.util.concurrent.TimeUnit.DAYS;
+
import android.Manifest;
import android.content.Context;
import android.content.Intent;
@@ -74,6 +76,7 @@
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
Manifest.permission.ACCESS_FINE_LOCATION);
+ intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
mContext.startActivity(intent);
});
}
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index fbc6edd..237e08a 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -30,7 +30,7 @@
private static List<SubscriptionInfo> sResultsForTesting;
@VisibleForTesting
- static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) {
+ public static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) {
sResultsForTesting = results;
}
diff --git a/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java
new file mode 100644
index 0000000..008a3e4
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.network.telephony;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+public class CallsDefaultSubscriptionController extends DefaultSubscriptionController {
+
+ public CallsDefaultSubscriptionController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ protected SubscriptionInfo getDefaultSubscriptionInfo() {
+ return mManager.getDefaultVoiceSubscriptionInfo();
+ }
+
+ @Override
+ protected int getDefaultSubscriptionId() {
+ return SubscriptionManager.getDefaultVoiceSubscriptionId();
+ }
+
+ @Override
+ protected void setDefaultSubscription(int subscriptionId) {
+ mManager.setDefaultVoiceSubId(subscriptionId);
+ }
+}
diff --git a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
new file mode 100644
index 0000000..bca6750
--- /dev/null
+++ b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 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.network.telephony;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.SubscriptionsChangeListener;
+
+import java.util.List;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * This implements common controller functionality for a Preference letting the user see/change
+ * what mobile network subscription is used by default for some service controlled by the
+ * SubscriptionManager. This can be used for services such as Calls or SMS.
+ */
+public abstract class DefaultSubscriptionController extends BasePreferenceController implements
+ LifecycleObserver, Preference.OnPreferenceChangeListener,
+ SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
+ private static final String TAG = "DefaultSubController";
+
+ protected SubscriptionsChangeListener mChangeListener;
+ protected ListPreference mPreference;
+ protected SubscriptionManager mManager;
+
+ public DefaultSubscriptionController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mManager = context.getSystemService(SubscriptionManager.class);
+ mChangeListener = new SubscriptionsChangeListener(context, this);
+ }
+
+ public void init(Lifecycle lifecycle) {
+ lifecycle.addObserver(this);
+ }
+
+ /** @return SubscriptionInfo for the default subscription for the service, or null if there
+ * isn't one. */
+ protected abstract SubscriptionInfo getDefaultSubscriptionInfo();
+
+ /** @return the id of the default subscription for the service, or
+ * SubscriptionManager.INVALID_SUBSCRIPTION_ID if there isn't one. */
+ protected abstract int getDefaultSubscriptionId();
+
+ /** Called to change the default subscription for the service. */
+ protected abstract void setDefaultSubscription(int subscriptionId);
+
+ @Override
+ public int getAvailabilityStatus() {
+ final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
+ if (subs.size() > 1) {
+ return AVAILABLE;
+ } else {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ }
+
+ @OnLifecycleEvent(ON_RESUME)
+ public void onResume() {
+ mChangeListener.start();
+ updateEntries();
+ }
+
+ @OnLifecycleEvent(ON_PAUSE)
+ public void onPause() {
+ mChangeListener.stop();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ updateEntries();
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final SubscriptionInfo info = getDefaultSubscriptionInfo();
+ if (info != null) {
+ return info.getDisplayName();
+ } else {
+ return mContext.getString(R.string.calls_and_sms_ask_every_time);
+ }
+ }
+
+ private void updateEntries() {
+ if (mPreference == null) {
+ return;
+ }
+ if (!isAvailable()) {
+ mPreference.setVisible(false);
+ return;
+ }
+ mPreference.setVisible(true);
+
+ final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
+
+ // We'll have one entry for each available subscription, plus one for a "ask me every
+ // time" entry at the end.
+ final CharSequence[] displayNames = new CharSequence[subs.size() + 1];
+ final CharSequence[] subscriptionIds = new CharSequence[subs.size() + 1];
+
+ final int serviceDefaultSubId = getDefaultSubscriptionId();
+ boolean subIsAvailable = false;
+
+ int i = 0;
+ for (; i < subs.size(); i++) {
+ displayNames[i] = subs.get(i).getDisplayName();
+ final int subId = subs.get(i).getSubscriptionId();
+ subscriptionIds[i] = Integer.toString(subId);
+ if (subId == serviceDefaultSubId) {
+ subIsAvailable = true;
+ }
+ }
+ // Add the extra "Ask every time" value at the end.
+ displayNames[i] = mContext.getString(R.string.calls_and_sms_ask_every_time);
+ subscriptionIds[i] = Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ mPreference.setEntries(displayNames);
+ mPreference.setEntryValues(subscriptionIds);
+
+ if (subIsAvailable) {
+ mPreference.setValue(Integer.toString(serviceDefaultSubId));
+ } else {
+ mPreference.setValue(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final int subscriptionId = Integer.parseInt((String) newValue);
+ setDefaultSubscription(subscriptionId);
+ refreshSummary(mPreference);
+ return true;
+ }
+
+ @Override
+ public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ if (mPreference != null) {
+ updateEntries();
+ refreshSummary(mPreference);
+ }
+ }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 9665c09..5201586 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -126,6 +126,10 @@
public void onAttach(Context context) {
super.onAttach(context);
+ if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2)) {
+ use(CallsDefaultSubscriptionController.class).init(getLifecycle());
+ use(SmsDefaultSubscriptionController.class).init(getLifecycle());
+ }
use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId);
use(ApnPreferenceController.class).init(mSubId);
diff --git a/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java
new file mode 100644
index 0000000..b999219
--- /dev/null
+++ b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.network.telephony;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+public class SmsDefaultSubscriptionController extends DefaultSubscriptionController {
+
+ public SmsDefaultSubscriptionController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ protected SubscriptionInfo getDefaultSubscriptionInfo() {
+ return mManager.getDefaultSmsSubscriptionInfo();
+ }
+
+ @Override
+ protected int getDefaultSubscriptionId() {
+ return SubscriptionManager.getDefaultSmsSubscriptionId();
+ }
+
+ @Override
+ protected void setDefaultSubscription(int subscriptionId) {
+ mManager.setDefaultSmsSubId(subscriptionId);
+ }
+}
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index 13ec543..171b61e 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -43,6 +43,7 @@
private static final String KEY_SUBTITLE = "subtitle";
private static final String KEY_DESCRIPTION = "description";
private static final String KEY_NEGATIVE_TEXT = "negative_text";
+ private static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
// Re-set by the application. Should be done upon orientation changes, etc
private Executor mClientExecutor;
@@ -127,6 +128,7 @@
.setDescription(mPromptInfo.getDescription())
.setNegativeButton(mPromptInfo.getNegativeButtonText(), mClientExecutor,
mNegativeButtonListener)
+ .setRequireConfirmation(mPromptInfo.getRequireConfirmation())
.build();
mCancellationSignal = new CancellationSignal();
@@ -171,6 +173,10 @@
return mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
}
+ public boolean getRequireConfirmation() {
+ return mBundle.getBoolean(KEY_REQUIRE_CONFIRMATION);
+ }
+
public static class Builder {
private final Bundle mBundle = new Bundle();
@@ -194,6 +200,11 @@
return this;
}
+ public Builder setRequireConfirmation(boolean requireConfirmation) {
+ mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+ return this;
+ }
+
public PromptInfo build() {
return new PromptInfo(mBundle);
}
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index ca96344..cae7d33 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -18,13 +18,20 @@
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
@@ -66,6 +73,8 @@
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.FooterPreferenceMixinCompat;
import java.util.List;
@@ -152,6 +161,14 @@
private UserManager mUserManager;
private ChooseLockGenericController mController;
+ /**
+ * From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
+ */
+ @PasswordComplexity private int mRequestedMinComplexity;
+
+ /** From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_CALLER_APP_NAME}. */
+ private String mCallerAppName = null;
+
protected boolean mForFingerprint = false;
protected boolean mForFace = false;
@@ -195,6 +212,10 @@
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
+ mRequestedMinComplexity = getActivity().getIntent()
+ .getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+ mCallerAppName =
+ getActivity().getIntent().getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
mUserManager = UserManager.get(getActivity());
@@ -217,7 +238,8 @@
UserManager.get(getActivity()),
getArguments(),
getActivity().getIntent().getExtras()).getIdentifier();
- mController = new ChooseLockGenericController(getContext(), mUserId);
+ mController =
+ new ChooseLockGenericController(getContext(), mUserId, mRequestedMinComplexity);
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -291,6 +313,9 @@
// Forward the target user id to ChooseLockGeneric.
chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
+ chooseLockGenericIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
+ mRequestedMinComplexity);
+ chooseLockGenericIntent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
if (mUserPassword != null) {
chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
mUserPassword);
@@ -461,6 +486,13 @@
protected void addPreferences() {
addPreferencesFromResource(R.xml.security_settings_picker);
+ if (!TextUtils.isEmpty(mCallerAppName)) {
+ FooterPreferenceMixinCompat footerMixin =
+ new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
+ FooterPreference footer = footerMixin.createFooterPreference();
+ footer.setTitle(getFooterString());
+ }
+
// Used for testing purposes
findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
@@ -469,6 +501,27 @@
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
}
+ private String getFooterString() {
+ @StringRes int stringId;
+ switch (mRequestedMinComplexity) {
+ case PASSWORD_COMPLEXITY_HIGH:
+ stringId = R.string.unlock_footer_high_complexity_requested;
+ break;
+ case PASSWORD_COMPLEXITY_MEDIUM:
+ stringId = R.string.unlock_footer_medium_complexity_requested;
+ break;
+ case PASSWORD_COMPLEXITY_LOW:
+ stringId = R.string.unlock_footer_low_complexity_requested;
+ break;
+ case PASSWORD_COMPLEXITY_NONE:
+ default:
+ stringId = R.string.unlock_footer_none_complexity_requested;
+ break;
+ }
+
+ return getResources().getString(stringId, mCallerAppName);
+ }
+
private void updatePreferenceText() {
if (mForFingerprint) {
setPreferenceTitle(ScreenLockType.PATTERN,
@@ -624,6 +677,7 @@
ChooseLockPassword.IntentBuilder builder =
new ChooseLockPassword.IntentBuilder(getContext())
.setPasswordQuality(quality)
+ .setRequestedMinComplexity(mRequestedMinComplexity)
.setForFingerprint(mForFingerprint)
.setForFace(mForFace)
.setUserId(mUserId);
diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java
index eb7ff4e..91ca957 100644
--- a/src/com/android/settings/password/ChooseLockGenericController.java
+++ b/src/com/android/settings/password/ChooseLockGenericController.java
@@ -16,7 +16,11 @@
package com.android.settings.password;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
+import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.os.UserHandle;
@@ -36,6 +40,7 @@
private final Context mContext;
private final int mUserId;
+ @PasswordComplexity private final int mRequestedMinComplexity;
private ManagedLockPasswordProvider mManagedPasswordProvider;
private DevicePolicyManager mDpm;
@@ -43,6 +48,19 @@
this(
context,
userId,
+ PASSWORD_COMPLEXITY_NONE);
+ }
+
+ /**
+ * @param requestedMinComplexity specifies the min password complexity to be taken into account
+ * when determining the available screen lock types
+ */
+ public ChooseLockGenericController(Context context, int userId,
+ @PasswordComplexity int requestedMinComplexity) {
+ this(
+ context,
+ userId,
+ requestedMinComplexity,
context.getSystemService(DevicePolicyManager.class),
ManagedLockPasswordProvider.get(context, userId));
}
@@ -51,21 +69,26 @@
ChooseLockGenericController(
Context context,
int userId,
+ @PasswordComplexity int requestedMinComplexity,
DevicePolicyManager dpm,
ManagedLockPasswordProvider managedLockPasswordProvider) {
mContext = context;
mUserId = userId;
+ mRequestedMinComplexity = requestedMinComplexity;
mManagedPasswordProvider = managedLockPasswordProvider;
mDpm = dpm;
}
/**
- * @return The higher quality of either the specified {@code quality} or the quality required
- * by {@link DevicePolicyManager#getPasswordQuality}.
+ * Returns the highest quality among the specified {@code quality}, the quality required by
+ * {@link DevicePolicyManager#getPasswordQuality}, and the quality required by min password
+ * complexity.
*/
public int upgradeQuality(int quality) {
- // Compare min allowed password quality
- return Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
+ // Compare specified quality and dpm quality
+ int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
+ return Math.max(dpmUpgradedQuality,
+ PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
}
/**
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 0ae664c..129f9ac 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -16,14 +16,18 @@
package com.android.settings.password;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.PasswordMetrics;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -56,6 +60,7 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
import com.android.internal.widget.TextViewInputDisabler;
@@ -133,6 +138,11 @@
return this;
}
+ public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
+ mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
+ return this;
+ }
+
public Intent build() {
return mIntent;
}
@@ -190,12 +200,10 @@
private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0;
private int mPasswordMinLengthToFulfillAllPolicies = 0;
+ private boolean mPasswordNumSequenceAllowed = true;
+ @PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
protected int mUserId;
private byte[] mPasswordHistoryHashFactor;
- /**
- * Password requirements that we need to verify.
- */
- private int[] mPasswordRequirements;
private LockPatternUtils mLockPatternUtils;
private SaveAndFinishWorker mSaveAndFinishWorker;
@@ -372,7 +380,13 @@
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
- processPasswordRequirements(intent);
+ mRequestedMinComplexity = intent.getIntExtra(
+ EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+ mRequestedQuality = Math.max(
+ intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
+ mLockPatternUtils.getRequestedPasswordQuality(mUserId));
+
+ loadDpmPasswordRequirements();
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
if (intent.getBooleanExtra(
@@ -504,31 +518,6 @@
}
private void setupPasswordRequirementsView(View view) {
- final List<Integer> passwordRequirements = new ArrayList<>();
- if (mPasswordMinUpperCase > 0) {
- passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
- }
- if (mPasswordMinLowerCase > 0) {
- passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
- }
- if (mPasswordMinLetters > 0) {
- if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) {
- passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
- }
- }
- if (mPasswordMinNumeric > 0) {
- passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
- }
- if (mPasswordMinSymbols > 0) {
- passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
- }
- if (mPasswordMinNonLetter > 0) {
- if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) {
- passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
- }
- }
- // Convert list to array.
- mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view);
mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
mPasswordRequirementAdapter = new PasswordRequirementAdapter();
@@ -603,13 +592,12 @@
/**
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
- *
- * @param intent the incoming intent
*/
- private void processPasswordRequirements(Intent intent) {
+ private void loadDpmPasswordRequirements() {
final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
- mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
- mRequestedQuality), dpmPasswordQuality);
+ if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ mPasswordNumSequenceAllowed = false;
+ }
mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
@@ -620,7 +608,7 @@
mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
- // Modify the value based on dpm policy.
+ // Modify the value based on dpm policy
switch (dpmPasswordQuality) {
case PASSWORD_QUALITY_ALPHABETIC:
if (mPasswordMinLetters == 0) {
@@ -646,19 +634,88 @@
mPasswordMinSymbols = 0;
mPasswordMinNonLetter = 0;
}
+
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
}
/**
+ * Merges the dpm requirements and the min complexity requirements.
+ *
+ * <p>Since there are more than one set of metrics to meet the min complexity requirement,
+ * and we are not hard-coding any one of them to be the requirements the user must fulfil,
+ * we are taking what the user has already entered into account when compiling the list of
+ * requirements from min complexity. Then we merge this list with the DPM requirements, and
+ * present the merged set as validation results to the user on the UI.
+ *
+ * <p>For example, suppose min complexity requires either ALPHABETIC(8+), or
+ * ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
+ * would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
+ * an alphanumeric password so we would update the min complexity required min length to 6.
+ * This might result in a little confusion for the user but the UI does not support showing
+ * multiple sets of requirements / validation results as options to users, this is the best
+ * we can do now.
+ */
+ private void mergeMinComplexityAndDpmRequirements(int userEnteredPasswordQuality) {
+ if (mRequestedMinComplexity == PASSWORD_COMPLEXITY_NONE) {
+ // dpm requirements are dominant if min complexity is none
+ return;
+ }
+
+ // reset dpm requirements
+ loadDpmPasswordRequirements();
+
+ PasswordMetrics minMetrics = PasswordMetrics.getMinimumMetrics(
+ mRequestedMinComplexity, userEnteredPasswordQuality, mRequestedQuality,
+ requiresNumeric(), requiresLettersOrSymbols());
+ mPasswordNumSequenceAllowed = mPasswordNumSequenceAllowed
+ && minMetrics.quality != PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ mPasswordMinLength = Math.max(mPasswordMinLength, minMetrics.length);
+ mPasswordMinLetters = Math.max(mPasswordMinLetters, minMetrics.letters);
+ mPasswordMinUpperCase = Math.max(mPasswordMinUpperCase, minMetrics.upperCase);
+ mPasswordMinLowerCase = Math.max(mPasswordMinLowerCase, minMetrics.lowerCase);
+ mPasswordMinNumeric = Math.max(mPasswordMinNumeric, minMetrics.numeric);
+ mPasswordMinSymbols = Math.max(mPasswordMinSymbols, minMetrics.symbols);
+ mPasswordMinNonLetter = Math.max(mPasswordMinNonLetter, minMetrics.nonLetter);
+
+ if (minMetrics.quality == PASSWORD_QUALITY_ALPHABETIC) {
+ if (!requiresLettersOrSymbols()) {
+ mPasswordMinLetters = 1;
+ }
+ }
+ if (minMetrics.quality == PASSWORD_QUALITY_ALPHANUMERIC) {
+ if (!requiresLettersOrSymbols()) {
+ mPasswordMinLetters = 1;
+ }
+ if (!requiresNumeric()) {
+ mPasswordMinNumeric = 1;
+ }
+ }
+
+ mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
+ }
+
+ private boolean requiresLettersOrSymbols() {
+ // This is the condition for the password to be considered ALPHABETIC according to
+ // PasswordMetrics.computeForPassword()
+ return mPasswordMinLetters + mPasswordMinUpperCase
+ + mPasswordMinLowerCase + mPasswordMinSymbols + mPasswordMinNonLetter > 0;
+ }
+
+ private boolean requiresNumeric() {
+ return mPasswordMinNumeric > 0;
+ }
+
+ /**
* Validates PIN/Password and returns the validation result.
*
* @param password the raw password the user typed in
* @return the validation result.
*/
- private int validatePassword(String password) {
+ @VisibleForTesting
+ int validatePassword(String password) {
int errorCode = NO_ERROR;
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-
+ mergeMinComplexityAndDpmRequirements(metrics.quality);
if (password.length() < mPasswordMinLength) {
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
@@ -668,14 +725,25 @@
errorCode |= TOO_LONG;
} else {
// The length requirements are fulfilled.
- final int dpmQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
- if (dpmQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX &&
- metrics.numeric == password.length()) {
+ if (!mPasswordNumSequenceAllowed
+ && !requiresLettersOrSymbols()
+ && metrics.numeric == password.length()) {
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
- // if DevicePolicyManager requires a complex numeric password. There can be
- // two cases in the UI: 1. User chooses to enroll a PIN, 2. User chooses to
- // enroll a password but enters a numeric-only pin. We should carry out the
- // sequence check in both cases.
+ // if DevicePolicyManager or min password complexity requires a complex numeric
+ // password. There can be two cases in the UI: 1. User chooses to enroll a
+ // PIN, 2. User chooses to enroll a password but enters a numeric-only pin. We
+ // should carry out the sequence check in both cases.
+ //
+ // Conditions for the !requiresLettersOrSymbols() to be necessary:
+ // - DPM requires NUMERIC_COMPLEX
+ // - min complexity not NONE, user picks PASSWORD type so ALPHABETIC or
+ // ALPHANUMERIC is required
+ // Imagine user has entered "12345678", if we don't skip the sequence check, the
+ // validation result would show both "requires a letter" and "sequence not
+ // allowed", while the only requirement the user needs to know is "requires a
+ // letter" because once the user has fulfilled the alphabetic requirement, the
+ // password would not be containing only digits so this check would not be
+ // performed anyway.
final int sequence = PasswordMetrics.maxLengthSequence(password);
if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
@@ -706,43 +774,24 @@
}
}
- // Check the requirements one by one.
- for (int i = 0; i < mPasswordRequirements.length; i++) {
- int passwordRestriction = mPasswordRequirements[i];
- switch (passwordRestriction) {
- case MIN_LETTER_IN_PASSWORD:
- if (metrics.letters < mPasswordMinLetters) {
- errorCode |= NOT_ENOUGH_LETTER;
- }
- break;
- case MIN_UPPER_LETTERS_IN_PASSWORD:
- if (metrics.upperCase < mPasswordMinUpperCase) {
- errorCode |= NOT_ENOUGH_UPPER_CASE;
- }
- break;
- case MIN_LOWER_LETTERS_IN_PASSWORD:
- if (metrics.lowerCase < mPasswordMinLowerCase) {
- errorCode |= NOT_ENOUGH_LOWER_CASE;
- }
- break;
- case MIN_SYMBOLS_IN_PASSWORD:
- if (metrics.symbols < mPasswordMinSymbols) {
- errorCode |= NOT_ENOUGH_SYMBOLS;
- }
- break;
- case MIN_NUMBER_IN_PASSWORD:
- if (metrics.numeric < mPasswordMinNumeric) {
- errorCode |= NOT_ENOUGH_DIGITS;
- }
- break;
- case MIN_NON_LETTER_IN_PASSWORD:
- if (metrics.nonLetter < mPasswordMinNonLetter) {
- errorCode |= NOT_ENOUGH_NON_LETTER;
- }
- break;
- }
+ if (metrics.letters < mPasswordMinLetters) {
+ errorCode |= NOT_ENOUGH_LETTER;
}
-
+ if (metrics.upperCase < mPasswordMinUpperCase) {
+ errorCode |= NOT_ENOUGH_UPPER_CASE;
+ }
+ if (metrics.lowerCase < mPasswordMinLowerCase) {
+ errorCode |= NOT_ENOUGH_LOWER_CASE;
+ }
+ if (metrics.symbols < mPasswordMinSymbols) {
+ errorCode |= NOT_ENOUGH_SYMBOLS;
+ }
+ if (metrics.numeric < mPasswordMinNumeric) {
+ errorCode |= NOT_ENOUGH_DIGITS;
+ }
+ if (metrics.nonLetter < mPasswordMinNonLetter) {
+ errorCode |= NOT_ENOUGH_NON_LETTER;
+ }
return errorCode;
}
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 8d0fa60..32e8eaf 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -46,6 +46,18 @@
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
/**
+ * Intent extra for passing the requested min password complexity to later steps in the set new
+ * screen lock flow.
+ */
+ public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity";
+
+ /**
+ * Intent extra for passing the label of the calling app to later steps in the set new screen
+ * lock flow.
+ */
+ public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name";
+
+ /**
* When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag
* controls if we relax the enforcement of
* {@link Utils#enforceSameOwner(android.content.Context, int)}.
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 5eb1f32..0d9b21d 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -136,6 +136,10 @@
Intent intent = getIntent();
mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
+
+ final boolean requireConfirmation =
+ !intent.getBooleanExtra(KeyguardManager.EXTRA_USE_IMPLICIT, true);
+
String alternateButton = intent.getStringExtra(
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
@@ -170,7 +174,7 @@
&& !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
mCredentialMode = CREDENTIAL_MANAGED;
if (isBiometricAllowed(effectiveUserId)) {
- showBiometricPrompt();
+ showBiometricPrompt(requireConfirmation);
launchedBiometric = true;
} else {
showConfirmCredentials();
@@ -181,7 +185,7 @@
if (isBiometricAllowed(effectiveUserId)) {
// Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
// onAuthenticationError and do the right thing automatically.
- showBiometricPrompt();
+ showBiometricPrompt(requireConfirmation);
launchedBiometric = true;
} else {
showConfirmCredentials();
@@ -242,7 +246,7 @@
&& !isBiometricDisabledByAdmin(effectiveUserId);
}
- private void showBiometricPrompt() {
+ private void showBiometricPrompt(boolean requireConfirmation) {
mBiometricManager.setActiveUser(mUserId);
mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
@@ -255,6 +259,7 @@
.setSubtitle(mDetails)
.setNegativeButtonText(getResources()
.getString(R.string.confirm_device_credential_use_alternate_method))
+ .setRequireConfirmation(requireConfirmation)
.build();
mBiometricFragment = BiometricFragment.newInstance(info);
newFragment = true;
diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java
new file mode 100644
index 0000000..5f118cf
--- /dev/null
+++ b/src/com/android/settings/password/PasswordUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.settings.Utils;
+
+public final class PasswordUtils extends com.android.settingslib.Utils {
+
+ private static final String TAG = "Settings";
+
+ private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
+
+ /**
+ * Returns whether the uid which the activity with {@code activityToken} is launched from has
+ * been granted the {@code permission}.
+ */
+ public static boolean isCallingAppPermitted(Context context, IBinder activityToken,
+ String permission) {
+ try {
+ return context.checkPermission(permission, /* pid= */ -1,
+ ActivityManager.getService().getLaunchedFromUid(activityToken))
+ == PackageManager.PERMISSION_GRANTED;
+ } catch (RemoteException e) {
+ Log.v(TAG, "Could not talk to activity manager.", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the label of the package which the activity with {@code activityToken} is launched
+ * from or {@code null} if it is launched from the settings app itself.
+ */
+ @Nullable
+ public static CharSequence getCallingAppLabel(Context context, IBinder activityToken) {
+ String pkg = getCallingAppPackageName(activityToken);
+ if (pkg == null || pkg.equals(SETTINGS_PACKAGE_NAME)) {
+ return null;
+ }
+
+ return Utils.getApplicationLabel(context, pkg);
+ }
+
+ /**
+ * Returns the package name which the activity with {@code activityToken} is launched from.
+ */
+ @Nullable
+ private static String getCallingAppPackageName(IBinder activityToken) {
+ String pkg = null;
+ try {
+ pkg = ActivityManager.getService().getLaunchedFromPackage(activityToken);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Could not talk to activity manager.", e);
+ }
+ return pkg;
+ }
+
+ /** Crashes the calling application and provides it with {@code message}. */
+ public static void crashCallingApplication(IBinder activityToken, String message) {
+ IActivityManager am = ActivityManager.getService();
+ try {
+ int uid = am.getLaunchedFromUid(activityToken);
+ int userId = UserHandle.getUserId(uid);
+ am.crashApplication(
+ uid,
+ /* initialPid= */ -1,
+ getCallingAppPackageName(activityToken),
+ userId,
+ message);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Could not talk to activity manager.", e);
+ }
+ }
+}
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index 99f67cb..8ea8514 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -16,13 +16,22 @@
package com.android.settings.password;
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
+import android.app.admin.PasswordMetrics;
import android.content.Intent;
import android.os.Bundle;
+import android.os.IBinder;
import android.util.Log;
import com.android.settings.Utils;
@@ -37,6 +46,21 @@
private String mNewPasswordAction;
private SetNewPasswordController mSetNewPasswordController;
+ /**
+ * From intent extra {@link DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}.
+ *
+ * <p>This is used only if caller has the required permission and activity is launched by
+ * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD}.
+ */
+ private @PasswordComplexity int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
+
+ /**
+ * Label of the app which launches this activity.
+ *
+ * <p>Value would be {@code null} if launched from settings app.
+ */
+ private String mCallerAppName = null;
+
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
@@ -48,6 +72,25 @@
finish();
return;
}
+
+ IBinder activityToken = getActivityToken();
+ mCallerAppName = (String) PasswordUtils.getCallingAppLabel(this, activityToken);
+ if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
+ && getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
+ boolean hasPermission = PasswordUtils.isCallingAppPermitted(
+ this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ if (hasPermission) {
+ mRequestedMinComplexity = PasswordMetrics.sanitizeComplexityLevel(getIntent()
+ .getIntExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
+ } else {
+ PasswordUtils.crashCallingApplication(activityToken,
+ "Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
+ + " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
+ finish();
+ return;
+ }
+ }
+
mSetNewPasswordController = SetNewPasswordController.create(
this, this, getIntent(), getActivityToken());
mSetNewPasswordController.dispatchSetNewPasswordIntent();
@@ -60,6 +103,12 @@
: new Intent(this, ChooseLockGeneric.class);
intent.setAction(mNewPasswordAction);
intent.putExtras(chooseLockFingerprintExtras);
+ if (mCallerAppName != null) {
+ intent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
+ }
+ if (mRequestedMinComplexity != PASSWORD_COMPLEXITY_NONE) {
+ intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, mRequestedMinComplexity);
+ }
startActivity(intent);
finish();
}
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index a0f8bae..33c3edb 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -16,11 +16,17 @@
package com.android.settings.password;
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.View;
@@ -48,6 +54,7 @@
* Other changes should be done to ChooseLockGeneric class instead and let this class inherit
* those changes.
*/
+// TODO(b/123225425): Restrict SetupChooseLockGeneric to be accessible by SUW only
public class SetupChooseLockGeneric extends ChooseLockGeneric {
private static final String KEY_UNLOCK_SET_DO_LATER = "unlock_set_do_later";
@@ -71,6 +78,20 @@
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
+
+ if(getIntent().hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)) {
+ IBinder activityToken = getActivityToken();
+ boolean hasPermission = PasswordUtils.isCallingAppPermitted(
+ this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ if (!hasPermission) {
+ PasswordUtils.crashCallingApplication(activityToken,
+ "Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
+ + " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
+ finish();
+ return;
+ }
+ }
+
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
layout.setFitsSystemWindows(false);
}
diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
index 3fac672..e43140f 100644
--- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
+++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
@@ -76,6 +76,7 @@
.setEmptyText(R.string.permission_bar_chart_empty_text)
.setDetailsOnClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
+ intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
mContext.startActivity(intent);
})
.build();
@@ -102,7 +103,7 @@
private void retrievePermissionUsageData() {
mContext.getSystemService(PermissionControllerManager.class).getPermissionUsages(
- false /* countSystem */, (int) DAYS.toSeconds(1),
+ false /* countSystem */, (int) DAYS.toMillis(1),
mContext.getMainExecutor() /* executor */, this /* callback */);
}
@@ -111,6 +112,9 @@
return null;
}
+ // STOPSHIP: Ignore the STORAGE group since it's going away.
+ usageInfos.removeIf(usage -> usage.getName().equals("android.permission-group.STORAGE"));
+
final BarViewInfo[] barViewInfos = new BarViewInfo[
Math.min(BarChartPreference.MAXIMUM_BAR_VIEWS, usageInfos.size())];
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 284fd23..6df45ba 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -59,10 +59,6 @@
mUri = uri;
}
- protected Uri getUri() {
- return mUri;
- }
-
/**
* Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link Uri}
* if exists
@@ -148,7 +144,14 @@
if (needNotify) {
mCachedResults = results;
- mContext.getContentResolver().notifyChange(mUri, null);
+ notifySliceChange();
}
}
-}
+
+ /**
+ * Notify that data was updated and attempt to sync changes to the Slice.
+ */
+ protected void notifySliceChange() {
+ mContext.getContentResolver().notifyChange(mUri, null);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SlicePreferenceController.java b/src/com/android/settings/slices/SlicePreferenceController.java
index d7fcc18..89294c7 100644
--- a/src/com/android/settings/slices/SlicePreferenceController.java
+++ b/src/com/android/settings/slices/SlicePreferenceController.java
@@ -82,6 +82,8 @@
@Override
public void onChanged(Slice slice) {
- mSlicePreference.onSliceUpdated(slice);
+ if (slice != null) {
+ mSlicePreference.onSliceUpdated(slice);
+ }
}
}
diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java
index 0d0022f..2f9cd7a 100644
--- a/src/com/android/settings/vpn2/AppDialogFragment.java
+++ b/src/com/android/settings/vpn2/AppDialogFragment.java
@@ -164,7 +164,8 @@
final int userId = getUserId();
try {
if (mPackageInfo.packageName.equals(VpnUtils.getConnectedPackage(mService, userId))) {
- mService.setAlwaysOnVpnPackage(userId, null, /* lockdownEnabled */ false);
+ mService.setAlwaysOnVpnPackage(userId, null, /* lockdownEnabled */ false,
+ /* lockdownWhitelist */ null);
mService.prepareVpn(mPackageInfo.packageName, VpnConfig.LEGACY_VPN, userId);
}
} catch (RemoteException e) {
diff --git a/src/com/android/settings/vpn2/AppManagementFragment.java b/src/com/android/settings/vpn2/AppManagementFragment.java
index 1571216..5f46446 100644
--- a/src/com/android/settings/vpn2/AppManagementFragment.java
+++ b/src/com/android/settings/vpn2/AppManagementFragment.java
@@ -225,7 +225,7 @@
private boolean setAlwaysOnVpn(boolean isEnabled, boolean isLockdown) {
return mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId,
- isEnabled ? mPackageName : null, isLockdown);
+ isEnabled ? mPackageName : null, isLockdown, /* lockdownWhitelist */ null);
}
private void updateUI() {
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
index ec927ae..01b20f0 100644
--- a/src/com/android/settings/vpn2/ConfigDialogFragment.java
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -199,7 +199,7 @@
final ConnectivityManager conn = ConnectivityManager.from(mContext);
conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null,
- /* lockdownEnabled */ false);
+ /* lockdownEnabled */ false, /* lockdownWhitelist */ null);
VpnUtils.setLockdownVpn(mContext, profile.key);
} else {
// update only if lockdown vpn has been changed
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 1c79c1d..88fa8b2 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -292,11 +292,12 @@
@Override
public void onWifiStateChanged(int state) {
- mContext.getContentResolver().notifyChange(getUri(), null);
+ notifySliceChange();
}
@Override
public void onConnectedChanged() {
+ notifySliceChange();
}
@Override
diff --git a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
index bf4dec6..c6a48a8 100644
--- a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -45,6 +46,9 @@
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowDevicePolicyManager;
+import java.util.Arrays;
+import java.util.Collections;
+
@RunWith(RobolectricTestRunner.class)
public class CrossProfileCalendarPreferenceControllerTest {
@@ -123,7 +127,17 @@
@Test
public void updateState_somePackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
dpm.setProfileOwner(TEST_COMPONENT_NAME);
- dpm.addCrossProfileCalendarPackage(TEST_COMPONENT_NAME, TEST_PACKAGE_NAME);
+ dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME,
+ Collections.singleton(TEST_PACKAGE_NAME));
+
+ mController.updateState(mPreference);
+ verify(mPreference).setDisabledByAdmin(null);
+ }
+
+ @Test
+ public void updateState_allPackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
+ dpm.setProfileOwner(TEST_COMPONENT_NAME);
+ dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME, null);
mController.updateState(mPreference);
verify(mPreference).setDisabledByAdmin(null);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
index 1da97f5..b6bbe8a 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settings.connecteddevice.usb;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
@@ -101,7 +103,8 @@
final Intent intent = new Intent();
intent.setAction(UsbManager.ACTION_USB_PORT_CHANGED);
final UsbPortStatus status = new UsbPortStatus(0, POWER_ROLE_SINK,
- DATA_ROLE_DEVICE, 0);
+ DATA_ROLE_DEVICE, 0, CONTAMINANT_PROTECTION_NONE,
+ CONTAMINANT_DETECTION_NOT_SUPPORTED);
intent.putExtra(UsbManager.EXTRA_PORT_STATUS, status);
mReceiver.onReceive(mContext, intent);
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplayActivationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/NightDisplayActivationPreferenceControllerTest.java
index 6e23e5f..e666d61 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplayActivationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplayActivationPreferenceControllerTest.java
@@ -15,12 +15,11 @@
package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.provider.Settings.Secure;
+import android.hardware.display.ColorDisplayManager;
import android.view.View;
import androidx.preference.PreferenceScreen;
@@ -47,17 +46,19 @@
private PreferenceScreen mScreen;
private LayoutPreference mPreference;
private Context mContext;
- private NightDisplayActivationPreferenceController mController;
+ private ColorDisplayManager mColorDisplayManager;
+ private NightDisplayActivationPreferenceController mPreferenceController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mColorDisplayManager = mContext.getSystemService(ColorDisplayManager.class);
mPreference = new LayoutPreference(mContext, R.layout.night_display_activation_button);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
- mController = new NightDisplayActivationPreferenceController(mContext,
+ mPreferenceController = new NightDisplayActivationPreferenceController(mContext,
"night_display_activation");
- mController.displayPreference(mScreen);
+ mPreferenceController.displayPreference(mScreen);
}
@After
@@ -69,14 +70,14 @@
public void isAvailable_configuredAvailable() {
SettingsShadowResources.overrideResource(
com.android.internal.R.bool.config_nightDisplayAvailable, true);
- assertThat(mController.isAvailable()).isTrue();
+ assertThat(mPreferenceController.isAvailable()).isTrue();
}
@Test
public void isAvailable_configuredUnavailable() {
SettingsShadowResources.overrideResource(
com.android.internal.R.bool.config_nightDisplayAvailable, false);
- assertThat(mController.isAvailable()).isFalse();
+ assertThat(mPreferenceController.isAvailable()).isFalse();
}
@Test
@@ -95,25 +96,23 @@
@Test
public void onClick_activates() {
- Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 0);
+ mColorDisplayManager.setNightDisplayActivated(false);
final View view = mPreference.findViewById(R.id.night_display_turn_on_button);
assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
view.performClick();
- assertThat(Secure.getInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, -1))
- .isEqualTo(1);
+ assertThat(mColorDisplayManager.isNightDisplayActivated()).isEqualTo(true);
}
@Test
public void onClick_deactivates() {
- Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 1);
+ mColorDisplayManager.setNightDisplayActivated(true);
- final View view = mPreference.findViewById(R.id.night_display_turn_on_button);
+ final View view = mPreference.findViewById(R.id.night_display_turn_off_button);
assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
view.performClick();
- assertThat(Secure.getInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, -1))
- .isEqualTo(0);
+ assertThat(mColorDisplayManager.isNightDisplayActivated()).isEqualTo(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java
index 85d497a..de5c81e 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplayAutoModePreferenceControllerTest.java
@@ -17,9 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
import android.provider.Settings.Secure;
-import com.android.internal.app.ColorDisplayController;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.After;
@@ -66,8 +66,8 @@
@Test
public void onPreferenceChange_changesAutoMode() {
mController.onPreferenceChange(null,
- String.valueOf(ColorDisplayController.AUTO_MODE_TWILIGHT));
- assertThat(Secure.getInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, -1))
- .isEqualTo(ColorDisplayController.AUTO_MODE_TWILIGHT);
+ String.valueOf(ColorDisplayManager.AUTO_MODE_TWILIGHT));
+ assertThat(mContext.getSystemService(ColorDisplayManager.class).getNightDisplayAutoMode())
+ .isEqualTo(ColorDisplayManager.AUTO_MODE_TWILIGHT);
}
}
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplayIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/NightDisplayIntensityPreferenceControllerTest.java
index 2edec2e..2e2d631 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplayIntensityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplayIntensityPreferenceControllerTest.java
@@ -17,10 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
import android.provider.Settings.Secure;
-
import com.android.settings.testutils.shadow.SettingsShadowResources;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -33,69 +32,69 @@
@Config(shadows = SettingsShadowResources.class)
public class NightDisplayIntensityPreferenceControllerTest {
- private Context mContext;
- private NightDisplayIntensityPreferenceController mController;
+ private Context mContext;
+ private NightDisplayIntensityPreferenceController mPreferenceController;
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
- mController = new NightDisplayIntensityPreferenceController(mContext,
- "night_display_temperature");
- }
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mPreferenceController = new NightDisplayIntensityPreferenceController(mContext,
+ "night_display_temperature");
+ }
- @After
- public void tearDown() {
- SettingsShadowResources.reset();
- }
+ @After
+ public void tearDown() {
+ SettingsShadowResources.reset();
+ }
- @Test
- public void isAvailable_configuredAvailable_isActivated_available() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_nightDisplayAvailable, true);
- Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 1);
- assertThat(mController.isAvailable()).isTrue();
- }
+ @Test
+ public void isAvailable_configuredAvailable_isActivated_available() {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_nightDisplayAvailable, true);
+ Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 1);
+ assertThat(mPreferenceController.isAvailable()).isTrue();
+ }
- @Test
- public void isAvailable_configuredAvailable_isNotActivated_available() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_nightDisplayAvailable, true);
- Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 0);
- assertThat(mController.isAvailable()).isTrue();
- }
+ @Test
+ public void isAvailable_configuredAvailable_isNotActivated_available() {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_nightDisplayAvailable, true);
+ Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_ACTIVATED, 0);
+ assertThat(mPreferenceController.isAvailable()).isTrue();
+ }
- @Test
- public void isAvailable_configuredUnavailable_unavailable() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_nightDisplayAvailable, false);
- assertThat(mController.isAvailable()).isFalse();
- }
+ @Test
+ public void isAvailable_configuredUnavailable_unavailable() {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_nightDisplayAvailable, false);
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
- @Test
- public void onPreferenceChange_changesTemperature() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.integer.config_nightDisplayColorTemperatureMin, 2950);
- SettingsShadowResources.overrideResource(
- com.android.internal.R.integer.config_nightDisplayColorTemperatureMax, 3050);
- // A slider-adjusted "20" here would be 1/5 from the left / least-intense, i.e. 3030.
- mController.onPreferenceChange(null, 20);
+ @Test
+ public void onPreferenceChange_changesTemperature() {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.integer.config_nightDisplayColorTemperatureMin, 2950);
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.integer.config_nightDisplayColorTemperatureMax, 3050);
+ // A slider-adjusted "20" here would be 1/5 from the left / least-intense, i.e. 3030.
+ mPreferenceController.onPreferenceChange(null, 20);
- assertThat(Secure.getInt(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, -1))
- .isEqualTo(3030);
- }
+ assertThat(
+ mContext.getSystemService(ColorDisplayManager.class).getNightDisplayColorTemperature())
+ .isEqualTo(3030);
+ }
- @Test
- public void isSliceableCorrectKey_returnsTrue() {
- final NightDisplayIntensityPreferenceController controller =
- new NightDisplayIntensityPreferenceController(mContext,"night_display_temperature");
- assertThat(controller.isSliceable()).isTrue();
- }
+ @Test
+ public void isSliceableCorrectKey_returnsTrue() {
+ final NightDisplayIntensityPreferenceController controller =
+ new NightDisplayIntensityPreferenceController(mContext, "night_display_temperature");
+ assertThat(controller.isSliceable()).isTrue();
+ }
- @Test
- public void isSliceableIncorrectKey_returnsFalse() {
- final NightDisplayIntensityPreferenceController controller =
- new NightDisplayIntensityPreferenceController(mContext, "bad_key");
- assertThat(controller.isSliceable()).isFalse();
- }
+ @Test
+ public void isSliceableIncorrectKey_returnsFalse() {
+ final NightDisplayIntensityPreferenceController controller =
+ new NightDisplayIntensityPreferenceController(mContext, "bad_key");
+ assertThat(controller.isSliceable()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/NightDisplayPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/NightDisplayPreferenceControllerTest.java
index c066e6c..eeaae67f 100644
--- a/tests/robotests/src/com/android/settings/display/NightDisplayPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/NightDisplayPreferenceControllerTest.java
@@ -3,12 +3,9 @@
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
-import android.provider.Settings.Secure;
-
-import com.android.internal.app.ColorDisplayController;
+import android.hardware.display.ColorDisplayManager;
import com.android.settings.R;
import com.android.settings.testutils.shadow.SettingsShadowResources;
-
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -28,32 +25,32 @@
@Test
public void nightDisplaySuggestion_isNotCompleted_ifAutoModeDisabled() {
final Application context = RuntimeEnvironment.application;
- Secure.putInt(context.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, ColorDisplayController.AUTO_MODE_DISABLED);
+ context.getSystemService(ColorDisplayManager.class)
+ .setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_DISABLED);
assertThat(NightDisplayPreferenceController.isSuggestionComplete(context)).isFalse();
}
@Test
public void nightDisplaySuggestion_isCompleted_ifAutoModeCustom() {
final Application context = RuntimeEnvironment.application;
- Secure.putInt(context.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, ColorDisplayController.AUTO_MODE_CUSTOM);
+ context.getSystemService(ColorDisplayManager.class)
+ .setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
assertThat(NightDisplayPreferenceController.isSuggestionComplete(context)).isTrue();
}
@Test
public void nightDisplaySuggestion_isCompleted_ifAutoModeTwilight() {
final Application context = RuntimeEnvironment.application;
- Secure.putInt(context.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, ColorDisplayController.AUTO_MODE_TWILIGHT);
+ context.getSystemService(ColorDisplayManager.class)
+ .setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_TWILIGHT);
assertThat(NightDisplayPreferenceController.isSuggestionComplete(context)).isTrue();
}
@Test
- public void nightDisplaySuggestion_isCompleted_ifDisabled() {
+ public void nightDisplaySuggestion_isCompleted_ifSuggestionDisabled() {
final Application context = RuntimeEnvironment.application;
- Secure.putInt(context.getContentResolver(),
- Secure.NIGHT_DISPLAY_AUTO_MODE, ColorDisplayController.AUTO_MODE_DISABLED);
+ context.getSystemService(ColorDisplayManager.class)
+ .setNightDisplayAutoMode(ColorDisplayManager.AUTO_MODE_DISABLED);
SettingsShadowResources.overrideResource(R.bool.config_night_light_suggestion_enabled, false);
assertThat(NightDisplayPreferenceController.isSuggestionComplete(context)).isTrue();
}
diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
index 3b33558..8cba1de 100644
--- a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
@@ -36,6 +36,7 @@
import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -77,6 +78,7 @@
/** Verifies the title text, details text are correct, and the click listener is set. */
@Test
+ @Ignore
public void updateState_whenAppListIsEmpty_shouldDisplayTitleTextAndDetailsText() {
doReturn(new ArrayList<>()).when(mRecentLocationApps).getAppListSorted();
mController.displayPreference(mScreen);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/DefaultSubscriptionControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/DefaultSubscriptionControllerTest.java
new file mode 100644
index 0000000..f7a2567
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/telephony/DefaultSubscriptionControllerTest.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 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.network.telephony;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.settings.R;
+import com.android.settings.network.SubscriptionUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Arrays;
+
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class DefaultSubscriptionControllerTest {
+ @Mock
+ private SubscriptionManager mManager;
+ @Mock
+ private PreferenceScreen mScreen;
+
+ private ListPreference mListPreference;
+ private Context mContext;
+ private DefaultSubscriptionController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mManager);
+ final String key = "prefkey";
+ mController = spy(new TestDefaultSubscriptionController(mContext, key));
+ mListPreference = spy(new ListPreference(mContext));
+ when(mScreen.findPreference(key)).thenReturn(mListPreference);
+ }
+
+ @After
+ public void tearDown() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
+ }
+
+ @Test
+ public void getAvailabilityStatus_onlyOneSubscription_notAvailable() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(
+ createMockSub(1, "sub1")));
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_twoSubscriptions_isAvailable() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(
+ createMockSub(1, "sub1"),
+ createMockSub(2, "sub2")));
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void displayPreference_twoSubscriptionsSub1Default_correctListPreferenceValues() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+
+ final CharSequence entry = mListPreference.getEntry();
+ final String value = mListPreference.getValue();
+ assertThat(entry).isEqualTo("sub1");
+ assertThat(value).isEqualTo("111");
+
+ final CharSequence[] entries = mListPreference.getEntries();
+ assertThat(entries.length).isEqualTo(3);
+ assertThat(entries[0]).isEqualTo("sub1");
+ assertThat(entries[1]).isEqualTo("sub2");
+ assertThat(entries[2]).isEqualTo(mContext.getString(R.string.calls_and_sms_ask_every_time));
+
+ final CharSequence[] entryValues = mListPreference.getEntryValues();
+ assertThat(entryValues.length).isEqualTo(3);
+ assertThat(entryValues[0]).isEqualTo("111");
+ assertThat(entryValues[1]).isEqualTo("222");
+ assertThat(entryValues[2]).isEqualTo(
+ Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+
+ @Test
+ public void displayPreference_twoSubscriptionsSub2Default_correctListPreferenceValues() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub2.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+
+ final CharSequence entry = mListPreference.getEntry();
+ final String value = mListPreference.getValue();
+ assertThat(entry).isEqualTo("sub2");
+ assertThat(value).isEqualTo("222");
+
+ final CharSequence[] entries = mListPreference.getEntries();
+ assertThat(entries.length).isEqualTo(3);
+ assertThat(entries[0]).isEqualTo("sub1");
+ assertThat(entries[1]).isEqualTo("sub2");
+ assertThat(entries[2]).isEqualTo(mContext.getString(R.string.calls_and_sms_ask_every_time));
+
+ final CharSequence[] entryValues = mListPreference.getEntryValues();
+ assertThat(entryValues.length).isEqualTo(3);
+ assertThat(entryValues[0]).isEqualTo("111");
+ assertThat(entryValues[1]).isEqualTo("222");
+ assertThat(entryValues[2]).isEqualTo(
+ Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+
+ @Test
+ public void onPreferenceChange_prefChangedToSub2_callbackCalledCorrectly() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ mListPreference.setValue("222");
+ mController.onPreferenceChange(mListPreference, "222");
+ verify(mController).setDefaultSubscription(eq(222));
+ }
+
+ @Test
+ public void onPreferenceChange_prefChangedToAlwaysAsk_callbackCalledCorrectly() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ mListPreference.setValue(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ mController.onPreferenceChange(mListPreference,
+ Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ verify(mController).setDefaultSubscription(
+ eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+
+ @Test
+ public void onSubscriptionsChanged_twoSubscriptionsDefaultChanges_selectedEntryGetsUpdated() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ assertThat( mListPreference.getEntry()).isEqualTo("sub1");
+ assertThat(mListPreference.getValue()).isEqualTo("111");
+
+ doReturn(sub2.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+ mController.onSubscriptionsChanged();
+ assertThat( mListPreference.getEntry()).isEqualTo("sub2");
+ assertThat(mListPreference.getValue()).isEqualTo("222");
+ }
+
+ @Test
+ public void onSubscriptionsChanged_goFromTwoSubscriptionsToOne_prefDisappears() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mListPreference.isVisible()).isTrue();
+
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
+ mController.onSubscriptionsChanged();
+
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mListPreference.isVisible()).isFalse();
+ }
+
+ @Test
+ public void onSubscriptionsChanged_goFromOneSubscriptionToTwo_prefAppears() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mListPreference.isVisible()).isFalse();
+
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ mController.onSubscriptionsChanged();
+
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mListPreference.isVisible()).isTrue();
+ }
+
+ @Test
+ public void onSubscriptionsChanged_goFromTwoToThreeSubscriptions_listGetsUpdated() {
+ final SubscriptionInfo sub1 = createMockSub(111, "sub1");
+ final SubscriptionInfo sub2 = createMockSub(222, "sub2");
+ final SubscriptionInfo sub3 = createMockSub(333, "sub3");
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ doReturn(sub1.getSubscriptionId()).when(mController).getDefaultSubscriptionId();
+
+ mController.displayPreference(mScreen);
+ assertThat(mListPreference.getEntries().length).isEqualTo(3);
+
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2, sub3));
+ mController.onSubscriptionsChanged();
+
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mListPreference.isVisible()).isTrue();
+ final CharSequence[] entries = mListPreference.getEntries();
+ final CharSequence[] entryValues = mListPreference.getEntryValues();
+ assertThat(entries.length).isEqualTo(4);
+ assertThat(entries[0].toString()).isEqualTo("sub1");
+ assertThat(entries[1].toString()).isEqualTo("sub2");
+ assertThat(entries[2].toString()).isEqualTo("sub3");
+ assertThat(entries[3].toString()).isEqualTo(
+ mContext.getString(R.string.calls_and_sms_ask_every_time));
+ assertThat(entryValues[0].toString()).isEqualTo("111");
+ assertThat(entryValues[1].toString()).isEqualTo("222");
+ assertThat(entryValues[2].toString()).isEqualTo("333");
+ assertThat(entryValues[3].toString()).isEqualTo(
+ Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+
+ private SubscriptionInfo createMockSub(int id, String displayName) {
+ final SubscriptionInfo sub = mock(SubscriptionInfo.class);
+ when(sub.getSubscriptionId()).thenReturn(id);
+ when(sub.getDisplayName()).thenReturn(displayName);
+ return sub;
+ }
+
+ private class TestDefaultSubscriptionController extends DefaultSubscriptionController {
+
+ public TestDefaultSubscriptionController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ protected SubscriptionInfo getDefaultSubscriptionInfo() {
+ return null;
+ }
+
+ @Override
+ protected int getDefaultSubscriptionId() {
+ return 0;
+ }
+
+ @Override
+ protected void setDefaultSubscription(int subscriptionId) {
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
index 0240bd8..55a4224 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
@@ -85,7 +85,13 @@
}
@Test
- public void onAttach_noCrash() {
+ public void onAttach_noV2Flag_noCrash() {
+ mFragment.onAttach(mContext);
+ }
+
+ @Test
+ public void onAttach_v2Flag_noCrash() {
+ FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true);
mFragment.onAttach(mContext);
}
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
index cbc5765..2b7bdeb 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
@@ -16,15 +16,22 @@
package com.android.settings.password;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.content.ComponentName;
import com.android.settings.R;
@@ -58,11 +65,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new ChooseLockGenericController(
- application,
- 0 /* userId */,
- mDevicePolicyManager,
- mManagedLockPasswordProvider);
+ mController = createController(PASSWORD_COMPLEXITY_NONE);
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false);
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false);
}
@@ -225,4 +228,44 @@
assertThat(upgradedQuality).named("upgradedQuality")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
}
+
+ @Test
+ public void upgradeQuality_complexityHigh_minQualityNumericComplex() {
+ when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_HIGH);
+
+ assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+ .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
+ }
+
+ @Test
+ public void upgradeQuality_complexityMedium_minQualityNumericComplex() {
+ when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_MEDIUM);
+
+ assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+ .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
+ }
+
+ @Test
+ public void upgradeQuality_complexityLow_minQualitySomething() {
+ when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_LOW);
+
+ assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+ .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ }
+
+ private ChooseLockGenericController createController(
+ @PasswordComplexity int minPasswordComplexity) {
+ return new ChooseLockGenericController(
+ application,
+ 0 /* userId */,
+ minPasswordComplexity,
+ mDevicePolicyManager,
+ mManagedLockPasswordProvider);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
index e324214..a1db12c 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
@@ -16,6 +16,15 @@
package com.android.settings.password;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.application;
@@ -27,8 +36,10 @@
import android.provider.Settings.Global;
import androidx.annotation.Nullable;
+import androidx.preference.Preference;
import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.search.SearchFeatureProvider;
@@ -36,6 +47,7 @@
import com.android.settings.testutils.shadow.ShadowStorageManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.widget.FooterPreference;
import org.junit.After;
import org.junit.Before;
@@ -113,6 +125,70 @@
}
@Test
+ public void updatePreferencesOrFinish_footerPreferenceAddedHighComplexityText() {
+ ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+ Intent intent = new Intent()
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ initActivity(intent);
+ CharSequence expectedTitle =
+ mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
+
+ mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+ FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+ assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+ }
+
+ @Test
+ public void updatePreferencesOrFinish_footerPreferenceAddedMediumComplexityText() {
+ ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+ Intent intent = new Intent()
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_MEDIUM);
+ initActivity(intent);
+ CharSequence expectedTitle =
+ mActivity.getString(R.string.unlock_footer_medium_complexity_requested, "app name");
+
+ mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+ FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+ assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+ }
+
+ @Test
+ public void updatePreferencesOrFinish_footerPreferenceAddedLowComplexityText() {
+ ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+ Intent intent = new Intent()
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
+ initActivity(intent);
+ CharSequence expectedTitle =
+ mActivity.getString(R.string.unlock_footer_low_complexity_requested, "app name");
+
+ mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+ FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+ assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+ }
+
+ @Test
+ public void updatePreferencesOrFinish_footerPreferenceAddedNoneComplexityText() {
+ ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+ Intent intent = new Intent()
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+ initActivity(intent);
+ CharSequence expectedTitle =
+ mActivity.getString(R.string.unlock_footer_none_complexity_requested, "app name");
+
+ mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+ FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+ assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+ }
+
+ @Test
public void onActivityResult_requestcode0_shouldNotFinish() {
initActivity(null);
@@ -165,6 +241,48 @@
assertThat(mActivity.isFinishing()).isTrue();
}
+ @Test
+ public void onPreferenceTreeClick_fingerprintPassesMinComplexityInfoOntoNextActivity() {
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
+ initActivity(intent);
+
+ Preference fingerprintPref = new Preference(application);
+ fingerprintPref.setKey("unlock_skip_fingerprint");
+ boolean result = mFragment.onPreferenceTreeClick(fingerprintPref);
+
+ assertThat(result).isTrue();
+ Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+ assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+ .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
+ .isEqualTo("app name");
+ }
+
+ @Test
+ public void onPreferenceTreeClick_facePassesMinComplexityInfoOntoNextActivity() {
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
+ .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
+ .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
+ initActivity(intent);
+
+ Preference facePref = new Preference(application);
+ facePref.setKey("unlock_skip_face");
+ boolean result = mFragment.onPreferenceTreeClick(facePref);
+
+ assertThat(result).isTrue();
+ Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+ assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+ .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
+ .isEqualTo("app name");
+ }
+
private void initActivity(@Nullable Intent intent) {
if (intent == null) {
intent = new Intent();
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 367cb4c..404d205 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -16,19 +16,37 @@
package com.android.settings.password;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
+import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.content.Intent;
import android.os.UserHandle;
-import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.google.android.setupdesign.GlifLayout;
@@ -44,13 +62,21 @@
import org.robolectric.shadows.ShadowDrawable;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {SettingsShadowResources.class, ShadowUtils.class})
+@Config(shadows = {
+ SettingsShadowResources.class,
+ ShadowUtils.class,
+ ShadowDevicePolicyManager.class,
+})
public class ChooseLockPasswordTest {
+ private ShadowDevicePolicyManager mShadowDpm;
+
@Before
public void setUp() {
SettingsShadowResources.overrideResource(
com.android.internal.R.string.config_headlineFontFamily, "");
+ mShadowDpm = ShadowDevicePolicyManager.getShadow();
+ mShadowDpm.setPasswordMaximumLength(16);
}
@After
@@ -72,7 +98,7 @@
assertThat(intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD))
.named("EXTRA_KEY_PASSWORD")
.isEqualTo("password");
- assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
+ assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
.named("PASSWORD_TYPE_KEY")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
@@ -84,7 +110,7 @@
public void intentBuilder_setChallenge_shouldAddExtras() {
Intent intent = new IntentBuilder(application)
.setChallenge(12345L)
- .setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC)
+ .setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC)
.setUserId(123)
.build();
@@ -94,15 +120,214 @@
assertThat(intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0L))
.named("EXTRA_KEY_CHALLENGE")
.isEqualTo(12345L);
- assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
+ assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
.named("PASSWORD_TYPE_KEY")
- .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+ .isEqualTo(PASSWORD_QUALITY_ALPHANUMERIC);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
.named("EXTRA_USER_ID")
.isEqualTo(123);
}
@Test
+ public void intentBuilder_setMinComplexityMedium_hasMinComplexityExtraMedium() {
+ Intent intent = new IntentBuilder(application)
+ .setRequestedMinComplexity(PASSWORD_COMPLEXITY_MEDIUM)
+ .build();
+
+ assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+ assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+ .isEqualTo(PASSWORD_COMPLEXITY_MEDIUM);
+ }
+
+ @Test
+ public void intentBuilder_setMinComplexityNotCalled() {
+ Intent intent = new IntentBuilder(application).build();
+
+ assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_noMinPasswordComplexity() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHABETIC);
+ mShadowDpm.setPasswordMinimumLength(10);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "",
+ "Must contain at least 1 letter",
+ "Must be at least 10 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_pin() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+ /* userEnteredPassword= */ "",
+ "PIN must be at least 8 digits");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_password() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "",
+ "Must contain at least 1 letter",
+ "Must be at least 4 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_dpmRestrictionsStricter_password() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC);
+ mShadowDpm.setPasswordMinimumLength(9);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "",
+ "Must contain at least 1 letter",
+ "Must contain at least 1 numerical digit",
+ "Must be at least 9 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_dpmLengthLonger_pin() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
+ mShadowDpm.setPasswordMinimumLength(11);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
+ /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+ /* userEnteredPassword= */ "",
+ "PIN must be at least 11 digits");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_dpmQualityComplex() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_COMPLEX);
+ mShadowDpm.setPasswordMinimumSymbols(2);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "",
+ "Must contain at least 2 special symbols",
+ "Must be at least 6 characters");
+ }
+
+ @Test
+ @Config(shadows = ShadowLockPatternUtils.class)
+ public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_pinRequested() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+ /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+ /* userEnteredPassword= */ "12345678",
+ "Ascending, descending, or repeated sequence of digits isn't allowed");
+ }
+
+ @Test
+ @Config(shadows = ShadowLockPatternUtils.class)
+ public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_passwordRequested() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "12345678",
+ "Ascending, descending, or repeated sequence of digits isn't allowed");
+ }
+
+ @Test
+ @Config(shadows = ShadowLockPatternUtils.class)
+ public void processAndValidatePasswordRequirements_numericComplexHighComplexity_pinRequested() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+ /* userEnteredPassword= */ "12345678",
+ "Ascending, descending, or repeated sequence of digits isn't allowed");
+ }
+
+ @Test
+ @Config(shadows = ShadowLockPatternUtils.class)
+ public void processAndValidatePasswordRequirements_numericHighComplexity_pinRequested() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+ /* userEnteredPassword= */ "12345678",
+ "Ascending, descending, or repeated sequence of digits isn't allowed");
+ }
+
+ @Test
+ @Config(shadows = ShadowLockPatternUtils.class)
+ public void processAndValidatePasswordRequirements_numericComplexLowComplexity_passwordRequested() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "12345678",
+ "Must contain at least 1 letter");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_empty() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "",
+ "Must contain at least 1 letter",
+ "Must be at least 6 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_numeric() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "1",
+ "Must contain at least 1 letter",
+ "Must be at least 6 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphabetic() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "b",
+ "Must be at least 6 characters");
+ }
+
+ @Test
+ public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphanumeric() {
+ mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+ assertPasswordValidationResult(
+ /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+ /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+ /* userEnteredPassword= */ "b1",
+ "Must be at least 6 characters");
+ }
+
+ @Test
public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
assertThat(drawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_fingerprint_header);
@@ -132,4 +357,18 @@
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
return Shadows.shadowOf(((GlifLayout) fragment.getView()).getIcon());
}
+
+ private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
+ int passwordType, String userEnteredPassword, String... expectedValidationResult) {
+ Intent intent = new Intent();
+ intent.putExtra(CONFIRM_CREDENTIALS, false);
+ intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
+ intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
+ ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
+ ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
+ int validateResult = fragment.validatePassword(userEnteredPassword);
+ String[] messages = fragment.convertErrorCodeToMessages(validateResult);
+
+ assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java b/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java
new file mode 100644
index 0000000..845d346
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 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 android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.settings.password.PasswordUtils.getCallingAppLabel;
+import static com.android.settings.password.PasswordUtils.isCallingAppPermitted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.settings.testutils.shadow.ShadowActivityManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowActivityManager.class})
+public class PasswordUtilsTest {
+
+ private static final String PACKAGE_NAME = "com.android.app";
+ private static final String PERMISSION = "com.testing.permission";
+ private static final int UID = 1234;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ApplicationInfo mApplicationInfo;
+ @Mock
+ private IActivityManager mActivityService;
+ @Mock
+ private IBinder mActivityToken;
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ ShadowActivityManager.setService(mActivityService);
+ }
+
+ @Test
+ public void getCallingAppLabel_activityServiceThrowsRemoteException_returnsNull()
+ throws Exception {
+ when(mActivityService.getLaunchedFromPackage(mActivityToken))
+ .thenThrow(new RemoteException());
+
+ assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+ }
+
+ @Test
+ public void getCallingAppLabel_activityServiceReturnsSettingsApp_returnsNull()
+ throws Exception {
+ when(mActivityService.getLaunchedFromPackage(mActivityToken))
+ .thenReturn("com.android.settings");
+
+ assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+ }
+
+ @Test
+ public void getCallingAppLabel_packageManagerThrowsNameNotFound_returnsNull() throws Exception {
+ when(mActivityService.getLaunchedFromPackage(mActivityToken))
+ .thenReturn(PACKAGE_NAME);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+ }
+
+ @Test
+ public void getCallingAppLabel_returnsLabel() throws Exception {
+ when(mActivityService.getLaunchedFromPackage(mActivityToken))
+ .thenReturn(PACKAGE_NAME);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(mApplicationInfo);
+ when(mApplicationInfo.loadLabel(mPackageManager)).thenReturn("label");
+
+ assertThat(getCallingAppLabel(mContext, mActivityToken)).isEqualTo("label");
+ }
+
+ @Test
+ public void isCallingAppPermitted_permissionGranted_returnsTrue() throws Exception {
+ when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
+ when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_GRANTED);
+
+ assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isTrue();
+ }
+
+ @Test
+ public void isCallingAppPermitted_permissionDenied_returnsFalse() throws Exception {
+ when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
+ when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_DENIED);
+
+ assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
+ }
+
+ @Test
+ public void isCallingAppPermitted_throwsRemoteException_returnsFalse() throws Exception {
+ when(mActivityService.getLaunchedFromUid(mActivityToken)).thenThrow(new RemoteException());
+
+ assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
index 99738e7..d1b2b74 100644
--- a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
@@ -16,6 +16,16 @@
package com.android.settings.password;
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
@@ -23,6 +33,8 @@
import android.os.Bundle;
import android.provider.Settings;
+import com.android.settings.testutils.shadow.ShadowPasswordUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -31,11 +43,14 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
@RunWith(RobolectricTestRunner.class)
public class SetNewPasswordActivityTest {
+ private static final String APP_LABEL = "label";
+
private int mProvisioned;
@Before
@@ -48,6 +63,7 @@
public void tearDown() {
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, mProvisioned);
+ ShadowPasswordUtils.reset();
}
@Test
@@ -77,4 +93,106 @@
assertThat(intent.getComponent())
.isEqualTo(new ComponentName(activity, SetupChooseLockGeneric.class));
}
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void testLaunchChooseLock_setNewPasswordExtraWithoutPermission() {
+ ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ SetNewPasswordActivity activity =
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ assertThat(shadowActivity.getNextStartedActivityForResult()).isNull();
+ }
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void testLaunchChooseLock_setNewPasswordExtraWithPermission() {
+ ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+ ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ SetNewPasswordActivity activity =
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+ assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+ .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+ }
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void testLaunchChooseLock_setNewPasswordExtraInvalidValue() {
+ ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+ ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, -1);
+ SetNewPasswordActivity activity =
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+ }
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void testLaunchChooseLock_setNewPasswordExtraNoneComplexity() {
+ ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+ ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+ SetNewPasswordActivity activity =
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+ }
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void testLaunchChooseLock_setNewParentProfilePasswordExtraWithPermission() {
+ ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+ ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ Intent intent = new Intent(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
+ intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ SetNewPasswordActivity activity =
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+ assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+ assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+ assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
new file mode 100644
index 0000000..63bdc38
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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 android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowPasswordUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowActivity;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowUserManager.class,
+ ShadowUtils.class,
+ ShadowLockPatternUtils.class,
+})
+public class SetupChooseLockGenericTest {
+
+ @After
+ public void tearDown() {
+ ShadowPasswordUtils.reset();
+ }
+
+ @Test
+ public void setupChooseLockGenericPasswordComplexityExtraWithoutPermission() {
+ Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
+ intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ SetupChooseLockGeneric activity =
+ Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ assertThat(shadowActivity.isFinishing()).isTrue();
+ }
+
+ @Test
+ @Config(shadows = {ShadowPasswordUtils.class})
+ public void setupChooseLockGenericPasswordComplexityExtraWithPermission() {
+ ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+
+ Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
+ intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+ SetupChooseLockGeneric activity =
+ Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
+
+ ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+ assertThat(shadowActivity.isFinishing()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
index 38d658c..76bdaef 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
@@ -17,6 +17,7 @@
package com.android.settings.testutils.shadow;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -24,6 +25,7 @@
@Implements(ActivityManager.class)
public class ShadowActivityManager {
private static int sCurrentUserId = 0;
+ private static IActivityManager sService = null;
@Implementation
protected static int getCurrentUser() {
@@ -33,4 +35,13 @@
public static void setCurrentUser(int userId) {
sCurrentUserId = userId;
}
+
+ @Implementation
+ public static IActivityManager getService() {
+ return sService;
+ }
+
+ public static void setService(IActivityManager service) {
+ sService = service;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
index 6d2dbef..ca75916 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
@@ -1,5 +1,7 @@
package com.android.settings.testutils.shadow;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -23,6 +25,10 @@
private boolean mIsAdminActiveAsUser = false;
private ComponentName mDeviceOwnerComponentName;
private int mDeviceOwnerUserId = -1;
+ private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
+ private int mPasswordMaxLength = 16;
+ private int mPasswordMinLength = 0;
+ private int mPasswordMinSymbols = 0;
public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) {
mSupportMessagesMap.put(Objects.hash(admin, userHandle), message);
@@ -70,6 +76,42 @@
mDeviceOwnerComponentName = admin;
}
+ @Implementation
+ public int getPasswordQuality(ComponentName admin, int userHandle) {
+ return mPasswordMinQuality;
+ }
+
+ public void setPasswordQuality(int quality) {
+ mPasswordMinQuality = quality;
+ }
+
+ @Implementation
+ public int getPasswordMinimumLength(ComponentName admin, int userHandle) {
+ return mPasswordMinLength;
+ }
+
+ public void setPasswordMinimumLength(int length) {
+ mPasswordMinLength = length;
+ }
+
+ @Implementation
+ public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) {
+ return mPasswordMinSymbols;
+ }
+
+ public void setPasswordMinimumSymbols(int numOfSymbols) {
+ mPasswordMinSymbols = numOfSymbols;
+ }
+
+ @Implementation
+ public int getPasswordMaximumLength(int quality) {
+ return mPasswordMaxLength;
+ }
+
+ public void setPasswordMaximumLength(int length) {
+ mPasswordMaxLength = length;
+ }
+
public static ShadowDevicePolicyManager getShadow() {
return (ShadowDevicePolicyManager) Shadow.extract(
RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
index 663ab91..7ce098d 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
@@ -59,4 +59,14 @@
public static void setDeviceEncryptionEnabled(boolean deviceEncryptionEnabled) {
sDeviceEncryptionEnabled = deviceEncryptionEnabled;
}
+
+ @Implementation
+ protected byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) {
+ return null;
+ }
+
+ @Implementation
+ protected boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) {
+ return false;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java
new file mode 100644
index 0000000..6a5c4ae
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.testutils.shadow;
+
+import android.content.Context;
+import android.os.IBinder;
+
+import com.android.settings.password.PasswordUtils;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@Implements(PasswordUtils.class)
+public class ShadowPasswordUtils {
+
+ private static String sCallingAppLabel;
+ private static Set<String> sGrantedPermissions;
+
+ public static void reset() {
+ sCallingAppLabel = null;
+ sGrantedPermissions = null;
+ }
+
+ @Implementation
+ protected static boolean isCallingAppPermitted(Context context, IBinder activityToken,
+ String permission) {
+ if (sGrantedPermissions == null) {
+ return false;
+ }
+ return sGrantedPermissions.contains(permission);
+ }
+
+ public static void addGrantedPermission(String... permissions) {
+ if (sGrantedPermissions == null) {
+ sGrantedPermissions = new HashSet<>();
+ }
+ sGrantedPermissions.addAll(Arrays.asList(permissions));
+ }
+
+ @Implementation
+ protected static String getCallingAppLabel(Context context, IBinder activityToken) {
+ return sCallingAppLabel;
+ }
+
+ public static void setCallingAppLabel(String label) {
+ sCallingAppLabel = label;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index ae352cc..dd99e55 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -139,4 +139,11 @@
verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
}
+
+ @Test
+ public void onConnectedChanged_shouldNotifyChange() {
+ mWifiScanWorker.onConnectedChanged();
+
+ verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
+ }
}