Merge "Settings - Move Activity embedding split min width config to resources" into main
diff --git a/Android.bp b/Android.bp
index c9a409c..a9c3ea0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,7 +46,6 @@
android_library {
name: "Settings-core",
- platform_apis: true,
defaults: [
"SettingsLib-search-defaults",
"SettingsLintDefaults",
diff --git a/res/layout/search_bar_two_pane_version.xml b/res/layout/search_bar_two_pane_version.xml
index f98985c..337294e 100644
--- a/res/layout/search_bar_two_pane_version.xml
+++ b/res/layout/search_bar_two_pane_version.xml
@@ -29,6 +29,9 @@
android:paddingStart="@dimen/search_bar_padding_start_two_pane"
android:paddingEnd="@dimen/search_bar_padding_end_two_pane"
android:background="@drawable/search_bar_selected_background"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:nextFocusForward="@+id/homepage_container"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
<TextView
@@ -40,4 +43,4 @@
android:layout_gravity="start"
android:text="@string/search_settings"/>
</Toolbar>
-</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
+</com.google.android.material.card.MaterialCardView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 746dbf5..fa55d88 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -132,6 +132,8 @@
<string name="bluetooth_pairing_pref_title">Pair new device</string>
<!-- Keywords for bluetooth pairing item [CHAR LIMIT=30] -->
<string name="keywords_add_bt_device">bluetooth</string>
+ <!-- Title for bluetooth auto on toggle [CHAR LIMIT=60] -->
+ <string name="bluetooth_screen_auto_on_title">Automatically turn on again tomorrow</string>
<!-- Button to help user to pair right ear of the hearing aid device. It will show when only one of the hearing aid device set is connected. [CHAR LIMIT=20] -->
@@ -1800,8 +1802,12 @@
<string name="bluetooth_device_context_pair_connect">Pair & connect</string>
<!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty [CHAR LIMIT=NONE]-->
<string name="bluetooth_empty_list_bluetooth_off">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices</string>
+ <!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty when auto-on feature is enabled [CHAR LIMIT=NONE]-->
+ <string name="bluetooth_empty_list_bluetooth_off_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.</string>
<!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
<string name="bluetooth_scanning_on_info_message">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
+ <!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_scanning_on_info_message_auto_on_available">When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.\n\nApps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings.</string>
<!-- Bluetooth settings. Link text to bring the user to "scanning settings" screen. [CHAR LIMIT=NONE]-->
<string name="bluetooth_scan_change">Change</string>
@@ -4362,6 +4368,10 @@
<string name="trackpad_pointer_speed">Pointer speed</string>
<!-- Title for the button to trigger the 'touch gesture' education. [CHAR LIMIT=35] -->
<string name="trackpad_touch_gesture">Learn touchpad gestures</string>
+ <!-- Search keywords for "touchpad" -->
+ <string name="keywords_touchpad">trackpad, track pad, mouse, cursor, scroll, swipe, right click, click, pointer</string>
+ <!-- Search keywords for 'Bottom-right tap', the name of the touchpad setting that allows the user to click the bottom right corner of the touchpad for more options. -->
+ <string name="keywords_trackpad_bottom_right_tap">right click, tap</string>
<!-- Title text for 'Go home' gesture education [CHAR LIMIT=35] -->
<string name="gesture_title_go_home">Go home</string>
@@ -5140,7 +5150,7 @@
<!-- Title for accessibility hearing device page footer. [CHAR LIMIT=40] -->
<string name="accessibility_hearing_device_about_title">About hearing devices</string>
<!-- Description for text in accessibility hearing aids footer. [CHAR LIMIT=NONE] -->
- <string name="accessibility_hearing_device_footer_summary">Make sure your hearing device is turned on and ready to pair</string>
+ <string name="accessibility_hearing_device_footer_summary">Make sure your hearing device is turned on and ready to pair. Only ASHA and LE Audio hearing devices show on this page.</string>
<!-- Title for the pair hearing device page. [CHAR LIMIT=25] -->
<string name="accessibility_hearing_device_pairing_page_title">Pair hearing device</string>
<!-- Title for the preference category containing the list of the available hearing during and after bluetooth scanning devices. [CHAR LIMIT=30] -->
@@ -11576,7 +11586,7 @@
<string name="primary_sim_calls_title">Calls</string>
<!-- Title of Texts item/dialog at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=30] -->
<string name="primary_sim_texts_title">Texts</string>
- <!-- Title of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=30] -->
+ <!-- Title of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=60] -->
<string name="primary_sim_automatic_data_title">Automatic data switching</string>
<!-- Body text of automatic data switching at dual sim onboarding's primary sim page or SIMs page. [CHAR LIMIT=NONE] -->
<string name="primary_sim_automatic_data_msg">Use data from either SIM depending on coverage and availability</string>
diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml
index 86dc8a1..51507fb 100644
--- a/res/xml/bluetooth_screen.xml
+++ b/res/xml/bluetooth_screen.xml
@@ -18,6 +18,11 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/bluetooth_settings_title">
+ <SwitchPreferenceCompat
+ android:key="bluetooth_auto_on_settings_toggle"
+ android:title="@string/bluetooth_screen_auto_on_title"
+ settings:controller="com.android.settings.bluetooth.BluetoothAutoOnPreferenceController"/>
+
<Preference
android:key="bluetooth_screen_bt_pair_rename_devices"
android:title="@string/bluetooth_device_name"
diff --git a/res/xml/trackpad_settings.xml b/res/xml/trackpad_settings.xml
index 84ea528..cca92a3 100644
--- a/res/xml/trackpad_settings.xml
+++ b/res/xml/trackpad_settings.xml
@@ -49,7 +49,8 @@
android:summary="@string/trackpad_bottom_right_tap_summary"
android:icon="@drawable/ic_trackpad_bottom_right_click"
settings:controller="com.android.settings.inputmethod.TrackpadBottomPreferenceController"
- android:order="30"/>
+ android:order="30"
+ settings:keywords="@string/keywords_trackpad_bottom_right_tap"/>
<com.android.settings.widget.SeekBarPreference
android:key="trackpad_pointer_speed"
@@ -64,4 +65,4 @@
android:title="@string/trackpad_touch_gesture"
android:icon="@drawable/ic_trackpad_touch_gestures_inverse"
settings:controller="com.android.settings.inputmethod.TouchGesturesButtonPreferenceController"/>
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/src/com/android/settings/ActionDisabledByAppOpsDialog.java b/src/com/android/settings/ActionDisabledByAppOpsDialog.java
index 15b8503..be3558e 100644
--- a/src/com/android/settings/ActionDisabledByAppOpsDialog.java
+++ b/src/com/android/settings/ActionDisabledByAppOpsDialog.java
@@ -21,7 +21,6 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
public class ActionDisabledByAppOpsDialog extends Activity
implements DialogInterface.OnDismissListener {
diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
index 0dbf05e..4e9cd92 100644
--- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
@@ -43,6 +43,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List;
@@ -164,16 +165,9 @@
if (permittedServices != null && !permittedServices.contains(packageName)) {
return false;
}
- try {
- final int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName);
- final boolean ecmEnabled = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- } catch (Exception e) {
- // Fallback in case if app ops is not available in testing.
- return true;
- }
+
+ return !RestrictedLockUtilsInternal.isEnhancedConfirmationRestricted(getContext(),
+ packageName, AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
}
private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) {
diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
index 6f21fb8..03bf142 100644
--- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
+++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
@@ -235,10 +235,11 @@
boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName());
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
preference.checkEcmRestrictionAndSetDisabled(
AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- preference.getPackageName(), preference.getUid());
+ preference.getPackageName());
if (preference.isDisabledByEcm()) {
serviceAllowed = false;
}
@@ -257,40 +258,39 @@
preference.setEnabled(false);
}
}
- return;
- }
-
- boolean appOpsAllowed;
- if (serviceAllowed) {
- try {
- final int mode = mAppOps.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- preference.getUid(), preference.getPackageName());
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- serviceAllowed = appOpsAllowed;
- } catch (Exception e) {
- // Allow service in case if app ops is not available in testing.
- appOpsAllowed = true;
- }
} else {
- appOpsAllowed = false;
- }
- if (serviceAllowed || serviceEnabled) {
- preference.setEnabled(true);
- } else {
- // Disable accessibility service that are not permitted.
- final RestrictedLockUtils.EnforcedAdmin admin =
- RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
- mContext, preference.getPackageName(), UserHandle.myUserId());
-
- if (admin != null) {
- preference.setDisabledByAdmin(admin);
- } else if (!appOpsAllowed) {
- preference.setDisabledByAppOps(true);
+ boolean appOpsAllowed;
+ if (serviceAllowed) {
+ try {
+ final int mode = mAppOps.noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ preference.getUid(), preference.getPackageName());
+ final boolean ecmEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ serviceAllowed = appOpsAllowed;
+ } catch (Exception e) {
+ // Allow service in case if app ops is not available in testing.
+ appOpsAllowed = true;
+ }
} else {
- preference.setEnabled(false);
+ appOpsAllowed = false;
+ }
+ if (serviceAllowed || serviceEnabled) {
+ preference.setEnabled(true);
+ } else {
+ // Disable accessibility service that are not permitted.
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
+ mContext, preference.getPackageName(), UserHandle.myUserId());
+
+ if (admin != null) {
+ preference.setDisabledByAdmin(admin);
+ } else if (!appOpsAllowed) {
+ preference.setDisabledByAppOps(true);
+ } else {
+ preference.setEnabled(false);
+ }
}
}
}
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index 6976149..e111942 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -174,7 +174,7 @@
if (shouldEnable && !hasAccess) {
mSwitchPref.checkEcmRestrictionAndSetDisabled(AppOpsManager.OPSTR_GET_USAGE_STATS,
- mPackageName, mPackageInfo.applicationInfo.uid);
+ mPackageName);
shouldEnable = !mSwitchPref.isDisabledByEcm();
}
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index 82d55f3..90d733e 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -24,6 +24,7 @@
import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -490,12 +491,23 @@
return true;
case ACCESS_RESTRICTED_SETTINGS:
showLockScreen(getContext(), () -> {
- final AppOpsManager appOpsManager = getContext().getSystemService(
- AppOpsManager.class);
- appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- getUid(),
- getPackageName(),
- AppOpsManager.MODE_ALLOWED);
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ EnhancedConfirmationManager manager = getContext().getSystemService(
+ EnhancedConfirmationManager.class);
+ try {
+ manager.clearRestriction(getPackageName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e);
+ }
+ } else {
+ final AppOpsManager appOpsManager = getContext().getSystemService(
+ AppOpsManager.class);
+ appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ getUid(),
+ getPackageName(),
+ AppOpsManager.MODE_ALLOWED);
+ }
getActivity().invalidateOptionsMenu();
final String toastString = getContext().getString(
R.string.toast_allows_restricted_settings_successfully,
@@ -527,14 +539,25 @@
}
private boolean shouldShowAccessRestrictedSettings() {
- try {
- final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
- getPackageName());
- return mode == AppOpsManager.MODE_IGNORED;
- } catch (Exception e) {
- // Fallback in case if app ops is not available in testing.
- return false;
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return getSystemService(EnhancedConfirmationManager.class)
+ .isClearRestrictionAllowed(getPackageName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Exception when retrieving package:" + getPackageName(), e);
+ return false;
+ }
+ } else {
+ try {
+ final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
+ getPackageName());
+ return mode == AppOpsManager.MODE_IGNORED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
}
}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
index 949577b..640f21d 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
@@ -222,7 +222,7 @@
pref.setOnPreferenceChangeListener((preference, newValue) -> false);
pref.setSingleLineTitle(true);
pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN,
- item.getPackageName(), item.getUid());
+ item.getPackageName());
}
/**
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
index 00f0625..70a31b8 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.os.AsyncTask;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
@@ -42,7 +43,7 @@
private NotificationManager mNm;
private PackageManager mPm;
// The appOp representing this preference
- private String mAppOpStr;
+ private String mSettingIdentifier;
public ApprovalPreferenceController(Context context, String key) {
super(context, key);
@@ -76,8 +77,9 @@
/**
* Set the associated appOp for the Setting
*/
- public ApprovalPreferenceController setAppOpStr(String appOpStr) {
- mAppOpStr = appOpStr;
+ @NonNull
+ public ApprovalPreferenceController setSettingIdentifier(@NonNull String settingIdentifier) {
+ mSettingIdentifier = settingIdentifier;
return this;
}
@@ -118,14 +120,15 @@
}
});
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
if (!isAllowedCn && !isEnabled) {
preference.setEnabled(false);
} else if (isEnabled) {
preference.setEnabled(true);
} else {
- preference.checkEcmRestrictionAndSetDisabled(mAppOpStr,
- mCn.getPackageName(), mPkgInfo.applicationInfo.uid);
+ preference.checkEcmRestrictionAndSetDisabled(mSettingIdentifier,
+ mCn.getPackageName());
}
} else {
preference.updateState(
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
index 89767dd..e885d5c 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -103,7 +103,7 @@
.setCn(mComponentName)
.setNm(context.getSystemService(NotificationManager.class))
.setPm(mPm)
- .setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
+ .setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
.setParent(this);
use(HeaderPreferenceController.class)
.setFragment(this)
diff --git a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
index 4c9f813..2a5f1db 100644
--- a/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
+++ b/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccess.java
@@ -25,7 +25,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.preference.DropDownPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen;
@@ -38,6 +37,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settingslib.RestrictedDropDownPreference;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -52,6 +52,8 @@
public class PremiumSmsAccess extends EmptyTextSettings
implements Callback, Callbacks, OnPreferenceChangeListener {
+ private static final String ECM_RESTRICTION_KEY = "android:premium_sms_access";
+
private ApplicationsState mApplicationsState;
private AppStateSmsPremBridge mSmsBackend;
private Session mSession;
@@ -205,7 +207,7 @@
}
- private class PremiumSmsPreference extends DropDownPreference {
+ private class PremiumSmsPreference extends RestrictedDropDownPreference {
private final AppEntry mAppEntry;
public PremiumSmsPreference(AppEntry appEntry, Context context) {
@@ -224,6 +226,7 @@
});
setValue(String.valueOf(getCurrentValue()));
setSummary("%s");
+ this.checkEcmRestrictionAndSetDisabled(ECM_RESTRICTION_KEY, appEntry.info.packageName);
}
private int getCurrentValue() {
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index 53892bd..3376d86 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -65,6 +66,7 @@
*/
public class BiometricUtils {
private static final String TAG = "BiometricUtils";
+ public static final String EXTRA_ENROLL_REASON = BiometricManager.EXTRA_ENROLL_REASON;
/** The character ' • ' to separate the setup choose options */
public static final String SEPARATOR = " \u2022 ";
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
index 62e9757..6862bc9 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
@@ -265,6 +265,8 @@
}
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) {
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index e8bd0ef..da3689a 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -165,7 +165,7 @@
disabledFeatures[i] = mDisabledFeatures.get(i);
}
- return new FaceEnrollSidecar(disabledFeatures);
+ return new FaceEnrollSidecar(disabledFeatures, getIntent());
}
@Override
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index eb50e08..b0996a4 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -484,6 +484,9 @@
protected Intent getEnrollingIntent() {
Intent intent = new Intent(this, FaceEnrollEducation.class);
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
+
return intent;
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
index de713dd..b7ba1b5 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
+import android.content.Intent;
import android.hardware.face.FaceManager;
import com.android.settings.biometrics.BiometricEnrollSidecar;
@@ -33,8 +34,11 @@
private FaceUpdater mFaceUpdater;
- public FaceEnrollSidecar(int[] disabledFeatures) {
+ private Intent mIntent;
+
+ public FaceEnrollSidecar(int[] disabledFeatures, Intent intent) {
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
+ mIntent = intent;
}
@Override
@@ -47,7 +51,7 @@
public void startEnrollment() {
super.startEnrollment();
mFaceUpdater.enroll(mUserId, mToken, mEnrollmentCancel,
- mEnrollmentCallback, mDisabledFeatures);
+ mEnrollmentCallback, mDisabledFeatures, mIntent);
}
private FaceManager.EnrollmentCallback mEnrollmentCallback
diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java
index 57c1195..ddb6812 100644
--- a/src/com/android/settings/biometrics/face/FaceUpdater.java
+++ b/src/com/android/settings/biometrics/face/FaceUpdater.java
@@ -17,8 +17,10 @@
package com.android.settings.biometrics.face;
import android.content.Context;
+import android.content.Intent;
import android.hardware.face.Face;
import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.os.CancellationSignal;
import android.view.Surface;
@@ -26,6 +28,7 @@
import androidx.annotation.Nullable;
import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.safetycenter.BiometricsSafetySource;
/**
@@ -49,19 +52,19 @@
/** Wrapper around the {@link FaceManager#enroll} method. */
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
- FaceManager.EnrollmentCallback callback, int[] disabledFeatures) {
+ FaceManager.EnrollmentCallback callback, int[] disabledFeatures, Intent intent) {
this.enroll(userId, hardwareAuthToken, cancel,
new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
- null, false);
+ null, false, intent);
}
/** Wrapper around the {@link FaceManager#enroll} method. */
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
FaceManager.EnrollmentCallback callback, int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent, Intent intent) {
mFaceManager.enroll(userId, hardwareAuthToken, cancel,
new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
- previewSurface, debugConsent);
+ previewSurface, debugConsent, toFaceEnrollOptions(intent));
}
/** Wrapper around the {@link FaceManager#remove} method. */
@@ -135,4 +138,14 @@
BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed
}
}
+
+ private FaceEnrollOptions toFaceEnrollOptions(Intent intent) {
+ final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1);
+ final FaceEnrollOptions.Builder builder = new FaceEnrollOptions.Builder();
+ builder.setEnrollReason(FaceEnrollOptions.ENROLL_REASON_UNKNOWN);
+ if (reason != -1) {
+ builder.setEnrollReason(reason);
+ }
+ return builder.build();
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 175e8f9..90b9346 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -352,7 +352,7 @@
@Override
protected BiometricEnrollSidecar getSidecar() {
final FingerprintEnrollSidecar sidecar = new FingerprintEnrollSidecar(this,
- FingerprintManager.ENROLL_ENROLL);
+ FingerprintManager.ENROLL_ENROLL, getIntent());
return sidecar;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index be3a769..6b7538a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -292,6 +292,8 @@
@Override
protected Intent getFingerprintEnrollingIntent() {
final Intent ret = super.getFingerprintEnrollingIntent();
+ ret.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
ret.putExtras(mCalibrator.getExtrasForNextIntent(true));
@@ -349,7 +351,7 @@
FingerprintEnrollEnrolling.TAG_SIDECAR);
if (mSidecar == null) {
mSidecar = new FingerprintEnrollSidecar(this,
- FingerprintManager.ENROLL_FIND_SENSOR);
+ FingerprintManager.ENROLL_FIND_SENSOR, getIntent());
getSupportFragmentManager().beginTransaction()
.add(mSidecar, FingerprintEnrollEnrolling.TAG_SIDECAR)
.commitAllowingStateLoss();
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 242b745..f92cfbf 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -59,7 +59,6 @@
import java.util.List;
public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
-
private static final String TAG = "FingerprintIntro";
@VisibleForTesting
@@ -388,6 +387,8 @@
intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
}
}
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
return intent;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
index 493302b..52d7d2b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
@@ -21,6 +21,8 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.os.SystemClock;
import android.util.Log;
@@ -28,6 +30,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
+import com.android.settings.biometrics.BiometricUtils;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Sidecar fragment to handle the state around fingerprint enrollment.
@@ -39,15 +44,18 @@
private @FingerprintManager.EnrollReason int mEnrollReason;
private final MessageDisplayController mMessageDisplayController;
private final boolean mMessageDisplayControllerFlag;
+ private final Intent mIntent;
/**
* Create a new FingerprintEnrollSidecar object.
- * @param context associated context
+ *
+ * @param context associated context
* @param enrollReason reason for enrollment
*/
public FingerprintEnrollSidecar(Context context,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason, Intent intent) {
mEnrollReason = enrollReason;
+ mIntent = intent;
int helpMinimumDisplayTime = context.getResources().getInteger(
R.integer.enrollment_help_minimum_time_display);
@@ -85,14 +93,21 @@
return;
}
+ if (mIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) == -1) {
+ final boolean isSuw = WizardManagerHelper.isAnySetupWizard(mIntent);
+ mIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ isSuw ? FingerprintEnrollOptions.ENROLL_REASON_SUW :
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS);
+ }
+
if (mEnrollReason == ENROLL_ENROLL && mMessageDisplayControllerFlag) {
//API calls need to be processed for {@link FingerprintEnrollEnrolling}
mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId,
- mMessageDisplayController, mEnrollReason);
+ mMessageDisplayController, mEnrollReason, mIntent);
} else {
//No processing required for {@link FingerprintEnrollFindSensor}
mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback,
- mEnrollReason);
+ mEnrollReason, mIntent);
}
}
@@ -100,7 +115,8 @@
mEnrollReason = enrollReason;
}
- @VisibleForTesting FingerprintManager.EnrollmentCallback mEnrollmentCallback
+ @VisibleForTesting
+ FingerprintManager.EnrollmentCallback mEnrollmentCallback
= new FingerprintManager.EnrollmentCallback() {
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
index 306b1a3..ea9abf1 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
@@ -17,13 +17,16 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
+import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import androidx.annotation.Nullable;
import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.safetycenter.BiometricsSafetySource;
/**
@@ -48,9 +51,10 @@
/** Wrapper around the {@link FingerprintManager#enroll} method. */
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
FingerprintManager.EnrollmentCallback callback,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason, Intent intent) {
mFingerprintManager.enroll(hardwareAuthToken, cancel, userId,
- new NotifyingEnrollmentCallback(mContext, callback), enrollReason);
+ new NotifyingEnrollmentCallback(mContext, callback), enrollReason,
+ toFingerprintEnrollOptions(intent));
}
/** Wrapper around the {@link FingerprintManager#remove} method. */
@@ -138,4 +142,14 @@
BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed
}
}
+
+ private FingerprintEnrollOptions toFingerprintEnrollOptions(Intent intent) {
+ final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1);
+ final FingerprintEnrollOptions.Builder builder = new FingerprintEnrollOptions.Builder();
+ builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN);
+ if (reason != -1) {
+ builder.setEnrollReason(reason);
+ }
+ return builder.build();
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
index abc31c9..60ced6e 100644
--- a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
@@ -33,11 +33,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import java.util.function.Function;
public class SfpsEnrollmentFeatureImpl implements SfpsEnrollmentFeature {
+ @VisibleForTesting
+ public static final int HELP_ANIMATOR_DURATION = 550;
@Nullable
private FingerprintManager mFingerprintManager = null;
@@ -96,12 +99,11 @@
@Override
public Animator getHelpAnimator(@NonNull View target) {
final float translationX = 40;
- final int duration = 550;
final ObjectAnimator help = ObjectAnimator.ofFloat(target,
"translationX" /* propertyName */,
0, translationX, -1 * translationX, translationX, 0f);
help.setInterpolator(new AccelerateInterpolator());
- help.setDuration(duration);
+ help.setDuration(HELP_ANIMATOR_DURATION);
help.setAutoCancel(false);
return help;
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
index 135a36c..900f7cf 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
@@ -18,12 +18,16 @@
import android.content.Context
import android.content.Intent
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
+import com.android.settings.biometrics.BiometricUtils
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
@@ -35,6 +39,8 @@
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.password.ChooseLockSettingsHelper
+import com.android.systemui.biometrics.shared.model.toFingerprintSensor
+import com.google.android.setupcompat.util.WizardManagerHelper
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CancellableContinuation
@@ -60,6 +66,7 @@
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val pressToAuthInteractor: PressToAuthInteractor,
private val fingerprintFlow: FingerprintFlow,
+ private val intent: Intent,
) : FingerprintManagerInteractor {
private val maxFingerprints =
@@ -158,12 +165,21 @@
}
val cancellationSignal = CancellationSignal()
+
+ if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
+ val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW else
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS)
+ }
+
fingerprintManager.enroll(
hardwareAuthToken,
cancellationSignal,
applicationContext.userId,
enrollmentCallback,
enrollReason.toOriginalReason(),
+ toFingerprintEnrollOptions(intent)
)
awaitClose {
// If the stream has not been ended, and the user has stopped collecting the flow
@@ -244,4 +260,15 @@
applicationContext.userId,
)
}
+
+ private fun toFingerprintEnrollOptions(intent: Intent): FingerprintEnrollOptions {
+ val reason: Int =
+ intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)
+ val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder()
+ builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN)
+ if (reason != -1) {
+ builder.setEnrollReason(reason)
+ }
+ return builder.build()
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index 9f3935b..65030de 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -191,6 +191,7 @@
GatekeeperPasswordProvider(LockPatternUtils(context)),
pressToAuthInteractor,
enrollType,
+ getIntent(),
)
var challenge: Long? = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
index a5f2021..bd90524 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
@@ -232,6 +232,7 @@
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
pressToAuthInteractor,
Settings,
+ getIntent()
)
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index 9b25ee8..1cfec52 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -23,6 +23,7 @@
import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS;
import android.app.Application;
+import android.content.Intent;
import android.content.res.Resources;
import android.hardware.fingerprint.FingerprintManager.EnrollReason;
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
@@ -212,10 +213,11 @@
res.getBoolean(R.bool.enrollment_progress_priority_over_help),
res.getBoolean(R.bool.enrollment_prioritize_acquire_messages),
res.getInteger(R.integer.enrollment_collect_time));
- mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason);
+ mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason,
+ new Intent());
} else {
mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback,
- reason);
+ reason, new Intent());
}
return mCancellationSignal;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
new file mode 100644
index 0000000..f6f62e8
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 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.bluetooth;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.flags.Flags;
+import com.android.settingslib.utils.ThreadUtils;
+
+public class BluetoothAutoOnPreferenceController extends TogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+ private static final String TAG = "BluetoothAutoOnPreferenceController";
+ @VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle";
+ static final String SETTING_NAME = "bluetooth_automatic_turn_on";
+ static final int UNSET = -1;
+ @VisibleForTesting static final int ENABLED = 1;
+ @VisibleForTesting static final int DISABLED = 0;
+ private final ContentObserver mContentObserver =
+ new ContentObserver(new Handler(/* async= */ true)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ var unused =
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ updateValue();
+ mContext.getMainExecutor()
+ .execute(
+ () -> {
+ if (mPreference != null) {
+ updateState(mPreference);
+ }
+ });
+ });
+ }
+ };
+ private int mAutoOnValue = UNSET;
+ @Nullable private TwoStatePreference mPreference;
+
+ public BluetoothAutoOnPreferenceController(
+ @NonNull Context context, @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public void onStart() {
+ mContext.getContentResolver()
+ .registerContentObserver(
+ Settings.Secure.getUriFor(SETTING_NAME),
+ /* notifyForDescendants= */ false,
+ mContentObserver);
+ }
+
+ @Override
+ public void onStop() {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ updateValue();
+ return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mAutoOnValue == ENABLED;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (getAvailabilityStatus() != AVAILABLE) {
+ Log.w(TAG, "Trying to set toggle value while feature not available.");
+ return false;
+ }
+ var unused =
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ boolean updated =
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ SETTING_NAME,
+ isChecked ? ENABLED : DISABLED,
+ UserHandle.myUserId());
+ if (updated) {
+ updateValue();
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return 0;
+ }
+
+ private void updateValue() {
+ mAutoOnValue =
+ Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId());
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
index 6fd5070..ac55758 100644
--- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
@@ -15,8 +15,13 @@
*/
package com.android.settings.bluetooth;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
+
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.view.View;
import androidx.annotation.VisibleForTesting;
@@ -29,6 +34,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.flags.Flags;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -36,8 +42,11 @@
* is delegated to the SwitchWidgetController it uses.
*/
public class BluetoothSwitchPreferenceController
- implements LifecycleObserver, OnStart, OnStop,
- SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener {
+ implements LifecycleObserver,
+ OnStart,
+ OnStop,
+ SwitchWidgetController.OnSwitchChangeListener,
+ View.OnClickListener {
private BluetoothEnabler mBluetoothEnabler;
private RestrictionUtils mRestrictionUtils;
@@ -46,18 +55,21 @@
private FooterPreference mFooterPreference;
private boolean mIsAlwaysDiscoverable;
- @VisibleForTesting
- AlwaysDiscoverable mAlwaysDiscoverable;
+ @VisibleForTesting AlwaysDiscoverable mAlwaysDiscoverable;
- public BluetoothSwitchPreferenceController(Context context,
+ public BluetoothSwitchPreferenceController(
+ Context context,
SwitchWidgetController switchController,
FooterPreference footerPreference) {
this(context, new RestrictionUtils(), switchController, footerPreference);
}
@VisibleForTesting
- public BluetoothSwitchPreferenceController(Context context, RestrictionUtils restrictionUtils,
- SwitchWidgetController switchController, FooterPreference footerPreference) {
+ public BluetoothSwitchPreferenceController(
+ Context context,
+ RestrictionUtils restrictionUtils,
+ SwitchWidgetController switchController,
+ FooterPreference footerPreference) {
mRestrictionUtils = restrictionUtils;
mSwitch = switchController;
mContext = context;
@@ -66,11 +78,13 @@
mSwitch.setupView();
updateText(mSwitch.isChecked());
- mBluetoothEnabler = new BluetoothEnabler(context,
- switchController,
- FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
- SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
- mRestrictionUtils);
+ mBluetoothEnabler =
+ new BluetoothEnabler(
+ context,
+ switchController,
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
+ SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
+ mRestrictionUtils);
mBluetoothEnabler.setToggleCallback(this);
mAlwaysDiscoverable = new AlwaysDiscoverable(context);
}
@@ -97,8 +111,8 @@
/**
* Set whether the device can be discovered. By default the value will be {@code false}.
*
- * @param isAlwaysDiscoverable {@code true} if the device can be discovered,
- * otherwise {@code false}
+ * @param isAlwaysDiscoverable {@code true} if the device can be discovered, otherwise {@code
+ * false}
*/
public void setAlwaysDiscoverable(boolean isAlwaysDiscoverable) {
mIsAlwaysDiscoverable = isAlwaysDiscoverable;
@@ -119,15 +133,35 @@
.launch();
}
- @VisibleForTesting void updateText(boolean isChecked) {
+ @VisibleForTesting
+ void updateText(boolean isChecked) {
if (!isChecked && Utils.isBluetoothScanningEnabled(mContext)) {
- mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
+ if (isAutoOnFeatureAvailable()) {
+ mFooterPreference.setTitle(
+ R.string.bluetooth_scanning_on_info_message_auto_on_available);
+ } else {
+ mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message);
+ }
mFooterPreference.setLearnMoreText(mContext.getString(R.string.bluetooth_scan_change));
mFooterPreference.setLearnMoreAction(v -> onClick(v));
} else {
- mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
+ if (isAutoOnFeatureAvailable()) {
+ mFooterPreference.setTitle(
+ R.string.bluetooth_empty_list_bluetooth_off_auto_on_available);
+ } else {
+ mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
+ }
mFooterPreference.setLearnMoreText("");
mFooterPreference.setLearnMoreAction(null);
}
}
+
+ private boolean isAutoOnFeatureAvailable() {
+ if (!Flags.bluetoothQsTileDialogAutoOnToggle()) {
+ return false;
+ }
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId())
+ != UNSET;
+ }
}
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
index 7a7eb8c..b3e66a9 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
@@ -20,7 +20,9 @@
import android.os.UserHandle;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
@@ -36,6 +38,7 @@
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
DataSaverBackend.Listener {
+ private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access";
private final ApplicationsState mApplicationsState;
private final AppEntry mEntry;
@@ -58,6 +61,7 @@
mParentFragment = parentFragment;
setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(
context, entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
+ mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, entry.info.packageName);
updateState();
setKey(generateKey(mEntry));
@@ -166,10 +170,24 @@
return mHelper.isDisabledByAdmin();
}
+ @VisibleForTesting
+ boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public void setDisabledByAdmin(EnforcedAdmin admin) {
mHelper.setDisabledByAdmin(admin);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param packageName the package to check the restriction for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, packageName);
+ }
+
// Sets UI state based on allowlist/denylist status.
public void updateState() {
setTitle(mEntry.label);
@@ -179,7 +197,8 @@
setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (mDataUsageState.isDataSaverDenylisted) {
setSummary(R.string.restrict_background_blocklisted);
- } else {
+ // If disabled by ECM, the summary is set directly by the switch.
+ } else if (!isDisabledByEcm()) {
setSummary("");
}
}
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
index fd2fcda..0fb30a8 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
@@ -151,6 +151,7 @@
} else {
preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext,
entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
+ preference.checkEcmRestrictionAndSetDisabled(entry.info.packageName);
preference.updateState();
}
preference.setOrder(i);
diff --git a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
index 44d77a3..9a4b90f 100644
--- a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
+++ b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
@@ -52,7 +52,7 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.trackpad_settings) {
+ new BaseSearchIndexProvider(R.xml.trackpad_gesture_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return FeatureFlagUtils
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index cea2b44..eda9d7a 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -38,17 +38,10 @@
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settings.R
-import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNames
-import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
-import com.android.settings.network.apn.ApnTypes.APN_TYPES_OPTIONS
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_MMS
-import com.android.settings.network.apn.ApnTypes.getApnTypeSelectedOptionsState
-import com.android.settings.network.apn.ApnTypes.updateApnType
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.LocalNavController
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
-import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
import com.android.settingslib.spa.widget.preference.SwitchPreference
@@ -79,7 +72,7 @@
val uriString = arguments!!.getString(URI)
val uriInit = Uri.parse(String(Base64.getDecoder().decode(uriString)))
val subId = arguments.getInt(SUB_ID)
- val apnDataInit = getApnDataInit(arguments, LocalContext.current, uriInit, subId)
+ val apnDataInit = getApnDataInit(arguments, LocalContext.current, uriInit, subId) ?: return
val apnDataCur = remember {
mutableStateOf(apnDataInit)
}
@@ -101,12 +94,7 @@
val context = LocalContext.current
val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
- val networkTypeSelectedOptionsState = remember {
- getNetworkTypeSelectedOptionsState(apnData.networkType)
- }
- var apnTypeSelectedOptionsState = remember {
- getApnTypeSelectedOptionsState(apnData.apnType)
- }
+ var apnTypeMmsSelected by remember { mutableStateOf(false) }
val navController = LocalNavController.current
var valid: String?
RegularScaffold(
@@ -114,11 +102,6 @@
actions = {
if (!apnData.customizedConfig.readOnlyApn) {
Button(onClick = {
- apnData = apnData.copy(
- networkType = ApnNetworkTypes.getNetworkType(
- networkTypeSelectedOptionsState
- )
- )
valid = validateAndSaveApnData(
apnDataInit,
apnData,
@@ -193,27 +176,12 @@
label = stringResource(R.string.apn_server),
enabled = apnData.serverEnabled
) { apnData = apnData.copy(server = it) }
- SettingsExposedDropdownMenuCheckBox(
- label = stringResource(R.string.apn_type),
- options = APN_TYPES_OPTIONS,
- selectedOptionsState = apnTypeSelectedOptionsState,
- enabled = apnData.apnTypeEnabled,
- errorMessage = validateAPNType(
- apnData.validEnabled, apnData.apnType,
- apnData.customizedConfig.readOnlyApnTypes, context
- )
- ) {
- val apnType = updateApnType(
- apnTypeSelectedOptionsState,
- apnData.customizedConfig.defaultApnTypes,
- apnData.customizedConfig.readOnlyApnTypes
- )
- apnTypeSelectedOptionsState = getApnTypeSelectedOptionsState(apnType)
- apnData = apnData.copy(
- apnType = apnType
- )
- }
- if (apnTypeSelectedOptionsState.contains(APN_TYPES_OPTIONS.indexOf(APN_TYPE_MMS))) {
+ ApnTypeCheckBox(
+ apnData = apnData,
+ onTypeChanged = { apnData = apnData.copy(apnType = it) },
+ onMmsSelectedChanged = { apnTypeMmsSelected = it },
+ )
+ if (apnTypeMmsSelected) {
SettingsOutlinedTextField(
value = apnData.mmsc,
label = stringResource(R.string.apn_mmsc),
@@ -249,13 +217,7 @@
selectedOptionIndex = apnData.apnRoaming,
enabled = apnData.apnRoamingEnabled
) { apnData = apnData.copy(apnRoaming = it) }
- SettingsExposedDropdownMenuCheckBox(
- label = stringResource(R.string.network_type),
- options = getNetworkTypeDisplayNames(),
- selectedOptionsState = networkTypeSelectedOptionsState,
- emptyVal = stringResource(R.string.network_type_unspecified),
- enabled = apnData.networkTypeEnabled
- ) {}
+ ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) }
SwitchPreference(
object : SwitchPreferenceModel {
override val title = context.resources.getString(R.string.carrier_enabled)
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
new file mode 100644
index 0000000..bc85f55
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.apn
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+
+@Composable
+fun ApnNetworkTypeCheckBox(apnData: ApnData, onNetworkTypeChanged: (Long) -> Unit) {
+ val options = remember { ApnNetworkTypes.getNetworkTypeOptions() }
+ val selectedStateMap = remember {
+ ApnNetworkTypes.networkTypeToSelectedStateMap(options, apnData.networkType)
+ }
+ SettingsDropdownCheckBox(
+ label = stringResource(R.string.network_type),
+ options = options,
+ emptyText = stringResource(R.string.network_type_unspecified),
+ enabled = apnData.networkTypeEnabled,
+ ) {
+ onNetworkTypeChanged(
+ ApnNetworkTypes.selectedStateMapToNetworkType(options, selectedStateMap)
+ )
+ }
+}
diff --git a/src/com/android/settings/network/apn/ApnNetworkTypes.kt b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
index 0ccd33a..e7a93b3 100644
--- a/src/com/android/settings/network/apn/ApnNetworkTypes.kt
+++ b/src/com/android/settings/network/apn/ApnNetworkTypes.kt
@@ -17,8 +17,9 @@
package com.android.settings.network.apn
import android.telephony.TelephonyManager
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.snapshots.SnapshotStateList
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.compose.runtime.snapshots.SnapshotStateMap
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
object ApnNetworkTypes {
private val Types = listOf(
@@ -39,32 +40,40 @@
TelephonyManager.NETWORK_TYPE_NR,
)
- fun getNetworkTypeDisplayNames(): List<String> =
- Types.map { TelephonyManager.getNetworkTypeName(it) }
+ fun getNetworkTypeOptions(): List<SettingsDropdownCheckOption> =
+ Types.map { SettingsDropdownCheckOption(TelephonyManager.getNetworkTypeName(it)) }
/**
* Gets the selected Network type Selected Options according to network type.
* @param networkType Initialized network type bitmask, often multiple network type options may
* be included.
*/
- fun getNetworkTypeSelectedOptionsState(networkType: Long): SnapshotStateList<Int> {
- val networkTypeSelectedOptionsState = mutableStateListOf<Int>()
+ fun networkTypeToSelectedStateMap(
+ options: List<SettingsDropdownCheckOption>,
+ networkType: Long,
+ ): SnapshotStateMap<SettingsDropdownCheckOption, Boolean> {
+ val stateMap = mutableStateMapOf<SettingsDropdownCheckOption, Boolean>()
Types.forEachIndexed { index, type ->
if (networkType and TelephonyManager.getBitMaskForNetworkType(type) != 0L) {
- networkTypeSelectedOptionsState.add(index)
+ stateMap[options[index]] = true
}
}
- return networkTypeSelectedOptionsState
+ return stateMap
}
/**
* Gets the network type according to the selected Network type Selected Options.
- * @param networkTypeSelectedOptionsState the selected Network type Selected Options.
+ * @param stateMap the selected Network type Selected Options.
*/
- fun getNetworkType(networkTypeSelectedOptionsState: SnapshotStateList<Int>): Long {
+ fun selectedStateMapToNetworkType(
+ options: List<SettingsDropdownCheckOption>,
+ stateMap: SnapshotStateMap<SettingsDropdownCheckOption, Boolean>,
+ ): Long {
var networkType = 0L
- networkTypeSelectedOptionsState.forEach { option ->
- networkType = networkType or TelephonyManager.getBitMaskForNetworkType(Types[option])
+ options.forEachIndexed { index, option ->
+ if (stateMap[option] == true) {
+ networkType = networkType or TelephonyManager.getBitMaskForNetworkType(Types[index])
+ }
}
return networkType
}
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index 2266983..2d41976 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -18,6 +18,7 @@
import android.content.ContentValues
import android.content.Context
+import android.database.Cursor
import android.net.Uri
import android.provider.Telephony
import android.telephony.SubscriptionManager
@@ -27,26 +28,7 @@
import com.android.settingslib.utils.ThreadUtils
import java.util.Locale
-const val NAME_INDEX = 1
-const val APN_INDEX = 2
-const val PROXY_INDEX = 3
-const val PORT_INDEX = 4
-const val USER_INDEX = 5
-const val SERVER_INDEX = 6
-const val PASSWORD_INDEX = 7
-const val MMSC_INDEX = 8
-const val MMSPROXY_INDEX = 9
-const val MMSPORT_INDEX = 10
-const val AUTH_TYPE_INDEX = 11
-const val TYPE_INDEX = 12
-const val PROTOCOL_INDEX = 13
-const val CARRIER_ENABLED_INDEX = 14
-const val NETWORK_TYPE_INDEX = 15
-const val ROAMING_PROTOCOL_INDEX = 16
-const val EDITED_INDEX = 17
-const val USER_EDITABLE_INDEX = 18
-
-val sProjection = arrayOf(
+val Projection = arrayOf(
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.APN, // 2
@@ -68,7 +50,7 @@
Telephony.Carriers.USER_EDITABLE, // 18
)
-const val TAG = "ApnRepository"
+private const val TAG = "ApnRepository"
/**
* Query apn related information based on uri.
@@ -79,56 +61,39 @@
fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
var apnData = ApnData()
val contentResolver = context.contentResolver
- val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
contentResolver.query(
uri,
- sProjection,
+ Projection,
null /* selection */,
null /* selectionArgs */,
null /* sortOrder */
).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
- val name = cursor.getString(NAME_INDEX)
- val apn = cursor.getString(APN_INDEX)
- val proxy = cursor.getString(PROXY_INDEX)
- val port = cursor.getString(PORT_INDEX)
- val userName = cursor.getString(USER_INDEX)
- val server = cursor.getString(SERVER_INDEX)
- val passWord = cursor.getString(PASSWORD_INDEX)
- val mmsc = cursor.getString(MMSC_INDEX)
- val mmsProxy = cursor.getString(MMSPROXY_INDEX)
- val mmsPort = cursor.getString(MMSPORT_INDEX)
- val authType = cursor.getInt(AUTH_TYPE_INDEX)
- val apnType = cursor.getString(TYPE_INDEX)
- val apnProtocol = convertProtocol2Options(cursor.getString(PROTOCOL_INDEX), context)
- val apnRoaming =
- convertProtocol2Options(cursor.getString(ROAMING_PROTOCOL_INDEX), context)
- val apnEnable = cursor.getInt(CARRIER_ENABLED_INDEX) == 1
- val networkType = cursor.getLong(NETWORK_TYPE_INDEX)
-
- val edited = cursor.getInt(EDITED_INDEX)
- val userEditable = cursor.getInt(USER_EDITABLE_INDEX)
-
- apnData = apnData.copy(
- name = name,
- apn = apn,
- proxy = proxy,
- port = port,
- userName = userName,
- passWord = passWord,
- server = server,
- mmsc = mmsc,
- mmsProxy = mmsProxy,
- mmsPort = mmsPort,
- authType = authType,
- apnType = apnType,
- apnProtocol = apnProtocolOptions.indexOf(apnProtocol),
- apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
- apnEnable = apnEnable,
- networkType = networkType,
- edited = edited,
- userEditable = userEditable,
+ apnData = ApnData(
+ id = cursor.getInt(Telephony.Carriers._ID),
+ name = cursor.getString(Telephony.Carriers.NAME),
+ apn = cursor.getString(Telephony.Carriers.APN),
+ proxy = cursor.getString(Telephony.Carriers.PROXY),
+ port = cursor.getString(Telephony.Carriers.PORT),
+ userName = cursor.getString(Telephony.Carriers.USER),
+ passWord = cursor.getString(Telephony.Carriers.PASSWORD),
+ server = cursor.getString(Telephony.Carriers.SERVER),
+ mmsc = cursor.getString(Telephony.Carriers.MMSC),
+ mmsProxy = cursor.getString(Telephony.Carriers.MMSPROXY),
+ mmsPort = cursor.getString(Telephony.Carriers.MMSPORT),
+ authType = cursor.getInt(Telephony.Carriers.AUTH_TYPE),
+ apnType = cursor.getString(Telephony.Carriers.TYPE),
+ apnProtocol = context.convertProtocol2Options(
+ cursor.getString(Telephony.Carriers.PROTOCOL)
+ ),
+ apnRoaming = context.convertProtocol2Options(
+ cursor.getString(Telephony.Carriers.ROAMING_PROTOCOL)
+ ),
+ apnEnable = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
+ networkType = cursor.getLong(Telephony.Carriers.NETWORK_TYPE_BITMASK),
+ edited = cursor.getInt(Telephony.Carriers.EDITED_STATUS),
+ userEditable = cursor.getInt(Telephony.Carriers.USER_EDITABLE),
)
}
}
@@ -138,42 +103,23 @@
return apnData
}
+private fun Cursor.getString(columnName: String) = getString(getColumnIndexOrThrow(columnName))
+private fun Cursor.getInt(columnName: String) = getInt(getColumnIndexOrThrow(columnName))
+private fun Cursor.getLong(columnName: String) = getLong(getColumnIndexOrThrow(columnName))
+
/**
- * Returns The UI choice (e.g., "IPv4/IPv6") corresponding to the given
- * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
- * return null.
- *
- * @return UI choice
+ * Returns The UI choice index corresponding to the given raw value of the protocol preference
+ * (e.g., "IPV4V6").
+ * If unknown, return -1.
*/
-private fun convertProtocol2Options(raw: String, context: Context): String {
- val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
- val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
- var uRaw = raw.uppercase(Locale.getDefault())
- uRaw = if (uRaw == "IPV4") "IP" else uRaw
- val protocolIndex = apnProtocolValues.indexOf(uRaw)
- return if (protocolIndex == -1) {
- ""
- } else {
- try {
- apnProtocolOptions[protocolIndex]
- } catch (e: ArrayIndexOutOfBoundsException) {
- ""
- }
- }
+private fun Context.convertProtocol2Options(protocol: String): Int {
+ var normalizedProtocol = protocol.uppercase(Locale.getDefault())
+ if (normalizedProtocol == "IPV4") normalizedProtocol = "IP"
+ return resources.getStringArray(R.array.apn_protocol_values).indexOf(normalizedProtocol)
}
-fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
- val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
- return if (protocolIndex == -1) {
- ""
- } else {
- try {
- apnProtocolValues[protocolIndex]
- } catch (e: ArrayIndexOutOfBoundsException) {
- ""
- }
- }
-}
+fun Context.convertOptions2Protocol(protocolIndex: Int): String =
+ resources.getStringArray(R.array.apn_protocol_values).getOrElse(protocolIndex) { "" }
fun updateApnDataToDatabase(
newApn: Boolean,
@@ -183,13 +129,13 @@
) {
ThreadUtils.postOnBackgroundThread {
if (newApn) {
- // Add a new apn to the database
+ Log.d(TAG, "Adding an new APN to the database $uriInit $values")
val newUri = context.contentResolver.insert(uriInit, values)
if (newUri == null) {
Log.e(TAG, "Can't add a new apn to database $uriInit")
}
} else {
- // Update the existing apn
+ Log.d(TAG, "Updating an existing APN to the database $uriInit $values")
context.contentResolver.update(
uriInit, values, null /* where */, null /* selection Args */
)
@@ -210,9 +156,12 @@
)
fun isItemExist(apnData: ApnData, context: Context): String? {
- val contentValueMap = apnData.getContentValueMap(context).filterKeys { it in NonDuplicatedKeys }
- val list = contentValueMap.entries.toList()
- val selection = list.joinToString(" AND ") { "${it.key} = ?" }
+ val selectionMap = apnData.getContentValueMap(context).filterKeys { it in NonDuplicatedKeys }
+ .mapKeys { "${it.key} = ?" }
+ .toMutableMap()
+ if (apnData.id != -1) selectionMap += "${Telephony.Carriers._ID} != ?" to apnData.id
+ val list = selectionMap.entries.toList()
+ val selection = list.joinToString(" AND ") { it.key }
val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
context.contentResolver.query(
Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, apnData.subId.toString()),
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 02e2814..ab16f1c 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -22,19 +22,14 @@
import android.os.Bundle
import android.provider.Telephony
import android.telephony.CarrierConfigManager
-import android.text.TextUtils
import android.util.Log
-import com.android.internal.util.ArrayUtils
import com.android.settings.R
-import com.android.settings.network.apn.ApnTypes.APN_TYPES
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_ALL
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_EMERGENCY
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_IA
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_IMS
-import com.android.settings.network.apn.ApnTypes.APN_TYPE_MCX
-import java.util.Locale
+import com.android.settings.network.apn.ApnTypes.getPreSelectedApnType
+
+private const val TAG = "ApnStatus"
data class ApnData(
+ val id: Int = -1,
val name: String = "",
val apn: String = "",
val proxy: String = "",
@@ -86,8 +81,8 @@
Telephony.Carriers.MMSPROXY to mmsProxy,
Telephony.Carriers.MMSPORT to mmsPort,
Telephony.Carriers.AUTH_TYPE to authType,
- Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
- Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
+ Telephony.Carriers.PROTOCOL to context.convertOptions2Protocol(apnProtocol),
+ Telephony.Carriers.ROAMING_PROTOCOL to context.convertOptions2Protocol(apnRoaming),
Telephony.Carriers.TYPE to apnType,
Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
Telephony.Carriers.CARRIER_ENABLED to apnEnable,
@@ -105,7 +100,7 @@
val isAddApnAllowed: Boolean = true,
val readOnlyApnTypes: List<String> = emptyList(),
val readOnlyApnFields: List<String> = emptyList(),
- val defaultApnTypes: List<String> = emptyList(),
+ val defaultApnTypes: List<String>? = null,
val defaultApnProtocol: String = "",
val defaultApnRoamingProtocol: String = "",
)
@@ -118,19 +113,18 @@
*
* @return Initialized CustomizedConfig information.
*/
-fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData {
-
- val uriType = arguments.getString(URI_TYPE)!!
+fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData? {
+ val uriType = arguments.getString(URI_TYPE) ?: return null
if (!uriInit.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
Log.e(TAG, "Insert request not for carrier table. Uri: $uriInit")
- return ApnData() //TODO: finish
+ return null
}
var apnDataInit = when (uriType) {
EDIT_URL -> getApnDataFromUri(uriInit, context)
INSERT_URL -> ApnData()
- else -> ApnData() //TODO: finish
+ else -> return null
}
if (uriType == INSERT_URL) {
@@ -143,13 +137,18 @@
apnDataInit =
apnDataInit.copy(customizedConfig = getCarrierCustomizedConfig(apnDataInit, configManager))
+ if (apnDataInit.newApn) {
+ apnDataInit = apnDataInit.copy(
+ apnType = getPreSelectedApnType(apnDataInit.customizedConfig)
+ )
+ }
+
apnDataInit = apnDataInit.copy(
apnEnableEnabled =
context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
)
// TODO: mIsCarrierIdApn
- disableInit(apnDataInit)
- return apnDataInit
+ return disableInit(apnDataInit)
}
/**
@@ -199,53 +198,7 @@
if (errorMsg == null) {
errorMsg = isItemExist(apnData, context)
}
- if (errorMsg == null) {
- errorMsg = validateAPNType(
- true,
- apnData.apnType,
- apnData.customizedConfig.readOnlyApnTypes,
- context
- )
- }
- return errorMsg
-}
-
-private fun getUserEnteredApnType(apnType: String, readOnlyApnTypes: List<String>): String {
- // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
- // but if user enter empty type, map it just for default
- var userEnteredApnType = apnType
- if (userEnteredApnType != "") userEnteredApnType =
- userEnteredApnType.trim { it <= ' ' }
- if (TextUtils.isEmpty(userEnteredApnType) || APN_TYPE_ALL == userEnteredApnType) {
- userEnteredApnType = getEditableApnType(readOnlyApnTypes)
- }
- Log.d(
- TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
- + userEnteredApnType
- )
- return userEnteredApnType
-}
-
-private fun getEditableApnType(readOnlyApnTypes: List<String>): String {
- val editableApnTypes = StringBuilder()
- var first = true
- for (apnType in APN_TYPES) {
- // add APN type if it is not read-only and is not wild-cardable
- if (!readOnlyApnTypes.contains(apnType)
- && apnType != APN_TYPE_IA
- && apnType != APN_TYPE_EMERGENCY
- && apnType != APN_TYPE_MCX
- && apnType != APN_TYPE_IMS
- ) {
- if (first) {
- first = false
- } else {
- editableApnTypes.append(",")
- }
- editableApnTypes.append(apnType)
- }
- }
- return editableApnTypes.toString()
+ return errorMsg?.apply { Log.d(TAG, "APN data not valid, reason: $this") }
}
/**
@@ -258,6 +211,10 @@
apnInit: ApnData,
configManager: CarrierConfigManager
): CustomizedConfig {
+ fun log(message: String) {
+ Log.d(TAG, "getCarrierCustomizedConfig: $message")
+ }
+
val b = configManager.getConfigForSubId(
apnInit.subId,
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
@@ -270,72 +227,61 @@
val customizedConfig = CustomizedConfig(
readOnlyApnTypes = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY
- )?.toList() ?: emptyList(), readOnlyApnFields = b.getStringArray(
+ )?.toList() ?: emptyList(),
+ readOnlyApnFields = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
- )?.toList() ?: emptyList(), defaultApnTypes = b.getStringArray(
+ )?.toList() ?: emptyList(),
+ defaultApnTypes = b.getStringArray(
CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY
- )?.toList() ?: emptyList(), defaultApnProtocol = b.getString(
+ )?.toList(),
+ defaultApnProtocol = b.getString(
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING
- ) ?: "", defaultApnRoamingProtocol = b.getString(
+ ) ?: "",
+ defaultApnRoamingProtocol = b.getString(
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING
- ) ?: "", isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL)
+ ) ?: "",
+ isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL),
)
- if (!ArrayUtils.isEmpty(customizedConfig.readOnlyApnTypes)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: read only APN type: " + customizedConfig.readOnlyApnTypes.joinToString(
- ", "
- )
- )
+ if (customizedConfig.readOnlyApnTypes.isNotEmpty()) {
+ log("read only APN type: " + customizedConfig.readOnlyApnTypes)
}
- if (!ArrayUtils.isEmpty(customizedConfig.defaultApnTypes)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn types: " + customizedConfig.defaultApnTypes.joinToString(
- ", "
- )
- )
+ customizedConfig.defaultApnTypes?.takeIf { it.isNotEmpty() }?.let {
+ log("default apn types: $it")
}
- if (!TextUtils.isEmpty(customizedConfig.defaultApnProtocol)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn protocol: ${customizedConfig.defaultApnProtocol}"
- )
+ if (customizedConfig.defaultApnProtocol.isNotEmpty()) {
+ log("default apn protocol: ${customizedConfig.defaultApnProtocol}")
}
- if (!TextUtils.isEmpty(customizedConfig.defaultApnRoamingProtocol)) {
- Log.d(
- TAG,
- "getCarrierCustomizedConfig: default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}"
- )
+ if (customizedConfig.defaultApnRoamingProtocol.isNotEmpty()) {
+ log("default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}")
}
if (!customizedConfig.isAddApnAllowed) {
- Log.d(TAG, "getCarrierCustomizedConfig: not allow to add new APN")
+ log("not allow to add new APN")
}
return customizedConfig
}
-fun disableInit(apnDataInit: ApnData): ApnData {
- var apnData = apnDataInit
- val isUserEdited = apnDataInit.edited == Telephony.Carriers.USER_EDITED
- Log.d(TAG, "disableInit: EDITED $isUserEdited")
+private fun ApnData.isReadOnly(): Boolean {
+ Log.d(TAG, "isReadOnly: edited $edited")
+ if (edited == Telephony.Carriers.USER_EDITED) return false
// if it's not a USER_EDITED apn, check if it's read-only
- if (!isUserEdited && (apnDataInit.userEditable == 0
- || apnTypesMatch(apnDataInit.customizedConfig.readOnlyApnTypes, apnDataInit.apnType))
- ) {
+ return userEditable == 0 ||
+ ApnTypes.isApnTypeReadOnly(apnType, customizedConfig.readOnlyApnTypes)
+}
+
+fun disableInit(apnDataInit: ApnData): ApnData {
+ if (apnDataInit.isReadOnly()) {
Log.d(TAG, "disableInit: read-only APN")
- apnData =
- apnDataInit.copy(customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true))
- apnData = disableAllFields(apnData)
- } else if (!ArrayUtils.isEmpty(apnData.customizedConfig.readOnlyApnFields)) {
- Log.d(
- TAG,
- "disableInit: mReadOnlyApnFields ${
- apnData.customizedConfig.readOnlyApnFields.joinToString(", ")
- })"
+ val apnData = apnDataInit.copy(
+ customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true)
)
- apnData = disableFields(apnData.customizedConfig.readOnlyApnFields, apnData)
+ return disableAllFields(apnData)
}
- return apnData
+ val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields
+ if (readOnlyApnFields.isNotEmpty()) {
+ Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)")
+ return disableFields(readOnlyApnFields, apnDataInit)
+ }
+ return apnDataInit
}
/**
@@ -402,23 +348,6 @@
return apnData
}
-private fun apnTypesMatch(apnTypeList: List<String>, apnType: String): Boolean {
- val normalizeApnTypeList = apnTypeList.map(::normalizeApnType)
- return hasAllApns(normalizeApnTypeList) ||
- apnType.split(",").map(::normalizeApnType).all { it in normalizeApnTypeList }
-}
-
-fun hasAllApns(apnTypes: List<String>): Boolean {
- if (APN_TYPE_ALL in apnTypes) {
- Log.d(TAG, "hasAllApns: true because apnTypes.contains(APN_TYPE_ALL)")
- return true
- }
- return APN_TYPES.all { it in apnTypes }
-}
-
-private fun normalizeApnType(apnType: String): String =
- apnType.trim().lowercase(Locale.getDefault())
-
fun deleteApn(uri: Uri, context: Context) {
val contentResolver = context.contentResolver
contentResolver.delete(uri, null, null)
@@ -439,24 +368,3 @@
return if (validEnabled && (apn == "")) context.resources.getString(R.string.error_apn_empty)
else null
}
-
-fun validateAPNType(
- validEnabled: Boolean,
- apnType: String,
- readOnlyApnTypes: List<String>,
- context: Context
-): String? {
- // if carrier does not allow editing certain apn types, make sure type does not include those
- if (validEnabled && !ArrayUtils.isEmpty(readOnlyApnTypes)
- && apnTypesMatch(
- readOnlyApnTypes,
- getUserEnteredApnType(apnType, readOnlyApnTypes)
- )
- ) {
- return String.format(
- context.resources.getString(R.string.error_adding_apn_type),
- readOnlyApnTypes.joinToString(", ")
- )
- }
- return null
-}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
new file mode 100644
index 0000000..4d0659c
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.apn
+
+import android.telephony.data.ApnSetting
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.network.apn.ApnTypes.toApnType
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+
+@Composable
+fun ApnTypeCheckBox(
+ apnData: ApnData,
+ onTypeChanged: (String) -> Unit,
+ onMmsSelectedChanged: (Boolean) -> Unit,
+) {
+ val context = LocalContext.current
+ val apnTypeOptions = remember {
+ ApnTypes.getOptions(context, apnData.apnType, apnData.customizedConfig.readOnlyApnTypes)
+ }
+
+ fun updateMmsSelected() {
+ val apnTypeOptionMms = apnTypeOptions.single { it.text == ApnSetting.TYPE_MMS_STRING }
+ onMmsSelectedChanged(apnTypeOptionMms.selected.value)
+ }
+ LaunchedEffect(Unit) { updateMmsSelected() }
+ SettingsDropdownCheckBox(
+ label = stringResource(R.string.apn_type),
+ options = apnTypeOptions,
+ enabled = apnData.apnTypeEnabled,
+ ) {
+ onTypeChanged(apnTypeOptions.toApnType())
+ updateMmsSelected()
+ }
+}
diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt
index d3dbe38..2c8fa2a 100644
--- a/src/com/android/settings/network/apn/ApnTypes.kt
+++ b/src/com/android/settings/network/apn/ApnTypes.kt
@@ -16,128 +16,112 @@
package com.android.settings.network.apn
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.snapshots.SnapshotStateList
+import android.content.Context
+import android.telephony.data.ApnSetting
+import android.util.Log
+import android.widget.Toast
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.text.intl.Locale
+import androidx.compose.ui.text.toLowerCase
+import com.android.settings.R
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
object ApnTypes {
- /**
- * APN types for data connections. These are usage categories for an APN
- * entry. One APN entry may support multiple APN types, eg, a single APN
- * may service regular internet traffic ("default") as well as MMS-specific
- * connections.<br></br>
- * APN_TYPE_ALL is a special type to indicate that this APN entry can
- * service all data connections.
- */
- const val APN_TYPE_ALL = "*"
+ private const val TAG = "ApnTypes"
- /** APN type for default data traffic */
- const val APN_TYPE_DEFAULT = "default"
-
- /** APN type for MMS traffic */
- const val APN_TYPE_MMS = "mms"
-
- /** APN type for SUPL assisted GPS */
- const val APN_TYPE_SUPL = "supl"
-
- /** APN type for DUN traffic */
- const val APN_TYPE_DUN = "dun"
-
- /** APN type for HiPri traffic */
- const val APN_TYPE_HIPRI = "hipri"
-
- /** APN type for FOTA */
- const val APN_TYPE_FOTA = "fota"
-
- /** APN type for IMS */
- const val APN_TYPE_IMS = "ims"
-
- /** APN type for CBS */
- const val APN_TYPE_CBS = "cbs"
-
- /** APN type for IA Initial Attach APN */
- const val APN_TYPE_IA = "ia"
-
- /** APN type for Emergency PDN. This is not an IA apn, but is used
- * for access to carrier services in an emergency call situation. */
- const val APN_TYPE_EMERGENCY = "emergency"
-
- /** APN type for Mission Critical Services */
- const val APN_TYPE_MCX = "mcx"
-
- /** APN type for XCAP */
- const val APN_TYPE_XCAP = "xcap"
-
- /** APN type for VSIM */
- const val APN_TYPE_VSIM = "vsim"
-
- /** APN type for BIP */
- const val APN_TYPE_BIP = "bip"
-
- /** APN type for ENTERPRISE */
- const val APN_TYPE_ENTERPRISE = "enterprise"
-
- val APN_TYPES = arrayOf(
- APN_TYPE_DEFAULT,
- APN_TYPE_MMS,
- APN_TYPE_SUPL,
- APN_TYPE_DUN,
- APN_TYPE_HIPRI,
- APN_TYPE_FOTA,
- APN_TYPE_IMS,
- APN_TYPE_CBS,
- APN_TYPE_IA,
- APN_TYPE_EMERGENCY,
- APN_TYPE_MCX,
- APN_TYPE_XCAP,
- APN_TYPE_VSIM,
- APN_TYPE_BIP,
- APN_TYPE_ENTERPRISE
+ private val APN_TYPES = arrayOf(
+ ApnSetting.TYPE_DEFAULT_STRING,
+ ApnSetting.TYPE_MMS_STRING,
+ ApnSetting.TYPE_SUPL_STRING,
+ ApnSetting.TYPE_DUN_STRING,
+ ApnSetting.TYPE_HIPRI_STRING,
+ ApnSetting.TYPE_FOTA_STRING,
+ ApnSetting.TYPE_IMS_STRING,
+ ApnSetting.TYPE_CBS_STRING,
+ ApnSetting.TYPE_IA_STRING,
+ ApnSetting.TYPE_EMERGENCY_STRING,
+ ApnSetting.TYPE_MCX_STRING,
+ ApnSetting.TYPE_XCAP_STRING,
+ ApnSetting.TYPE_VSIM_STRING,
+ ApnSetting.TYPE_BIP_STRING,
+ ApnSetting.TYPE_ENTERPRISE_STRING,
)
- val APN_TYPES_OPTIONS = listOf(APN_TYPE_ALL) + APN_TYPES
-
- fun getApnTypeSelectedOptionsState(apnType: String): SnapshotStateList<Int> {
- val apnTypeSelectedOptionsState = mutableStateListOf<Int>()
- if (apnType.contains(APN_TYPE_ALL))
- APN_TYPES_OPTIONS.forEachIndexed { index, _ ->
- apnTypeSelectedOptionsState.add(index)
- }
- else {
- APN_TYPES_OPTIONS.forEachIndexed { index, type ->
- if (apnType.contains(type)) {
- apnTypeSelectedOptionsState.add(index)
- }
- }
- if (apnTypeSelectedOptionsState.size == APN_TYPES.size)
- apnTypeSelectedOptionsState.add(APN_TYPES_OPTIONS.indexOf(APN_TYPE_ALL))
+ private fun splitToList(apnType: String): List<String> {
+ val types = apnType.split(',').map { it.trim().toLowerCase(Locale.current) }
+ if (ApnSetting.TYPE_ALL_STRING in types || APN_TYPES.all { it in types }) {
+ return listOf(ApnSetting.TYPE_ALL_STRING)
}
- return apnTypeSelectedOptionsState
+ return APN_TYPES.filter { it in types }
}
- fun updateApnType(
- apnTypeSelectedOptionsState: SnapshotStateList<Int>,
- defaultApnTypes: List<String>,
- readOnlyApnTypes: List<String>
- ): String {
- val apnType = apnTypeSelectedOptionsState.joinToString { APN_TYPES_OPTIONS[it] }
- if (apnType.contains(APN_TYPE_ALL)) return APN_TYPE_ALL
- return if (apnType == "" && defaultApnTypes.isNotEmpty())
- getEditableApnType(defaultApnTypes, readOnlyApnTypes)
- else
- apnType
+ fun isApnTypeReadOnly(apnType: String, readOnlyTypes: List<String>): Boolean {
+ val apnTypes = splitToList(apnType)
+ return ApnSetting.TYPE_ALL_STRING in readOnlyTypes ||
+ ApnSetting.TYPE_ALL_STRING in apnTypes && readOnlyTypes.isNotEmpty() ||
+ apnTypes.any { it in readOnlyTypes }
}
- private fun getEditableApnType(
- defaultApnTypes: List<String>,
- readOnlyApnTypes: List<String>
- ): String {
- return defaultApnTypes.filterNot { apnType ->
- readOnlyApnTypes.contains(apnType) || apnType in listOf(
- APN_TYPE_IA,
- APN_TYPE_EMERGENCY,
- APN_TYPE_MCX,
- APN_TYPE_IMS,
+ fun getOptions(context: Context, apnType: String, readOnlyTypes: List<String>) = buildList {
+ val apnTypes = splitToList(apnType)
+ add(
+ context.createSettingsDropdownCheckOption(
+ text = ApnSetting.TYPE_ALL_STRING,
+ isSelectAll = true,
+ changeable = readOnlyTypes.isEmpty(),
+ selected = ApnSetting.TYPE_ALL_STRING in apnTypes,
)
- }.joinToString()
+ )
+ for (type in APN_TYPES) {
+ add(
+ context.createSettingsDropdownCheckOption(
+ text = type,
+ changeable = ApnSetting.TYPE_ALL_STRING !in readOnlyTypes &&
+ type !in readOnlyTypes,
+ selected = ApnSetting.TYPE_ALL_STRING in apnTypes || type in apnTypes,
+ )
+ )
+ }
+ }.also { Log.d(TAG, "APN Type options: $it") }
+
+ private fun Context.createSettingsDropdownCheckOption(
+ text: String,
+ isSelectAll: Boolean = false,
+ changeable: Boolean,
+ selected: Boolean,
+ ) = SettingsDropdownCheckOption(
+ text = text,
+ isSelectAll = isSelectAll,
+ changeable = changeable,
+ selected = mutableStateOf(selected),
+ ) {
+ if (!changeable) {
+ val message = resources.getString(R.string.error_adding_apn_type, text)
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
+ }
}
-}
\ No newline at end of file
+
+ fun List<SettingsDropdownCheckOption>.toApnType(): String {
+ val (selectAllOptions, regularOptions) = partition { it.isSelectAll }
+ for (selectAllOption in selectAllOptions) {
+ if (selectAllOption.selected.value) return ApnSetting.TYPE_ALL_STRING
+ }
+ return regularOptions.filter { it.selected.value }.joinToString(",") { it.text }
+ }
+
+ private val NotPreSelectedTypes = setOf(
+ ApnSetting.TYPE_IMS_STRING,
+ ApnSetting.TYPE_IA_STRING,
+ ApnSetting.TYPE_EMERGENCY_STRING,
+ ApnSetting.TYPE_MCX_STRING,
+ )
+
+ fun getPreSelectedApnType(customizedConfig: CustomizedConfig): String =
+ (customizedConfig.defaultApnTypes
+ ?: defaultPreSelectedApnTypes(customizedConfig.readOnlyApnTypes))
+ .joinToString(",")
+
+ private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List<String>) =
+ if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList()
+ else APN_TYPES.filter { it !in readOnlyApnTypes + NotPreSelectedTypes }
+}
diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java
index 418a571..f765d6d 100644
--- a/src/com/android/settings/notification/zen/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java
@@ -39,8 +39,8 @@
import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settings.widget.RestrictedAppPreference;
import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList;
import java.util.Collections;
@@ -122,7 +122,7 @@
for (ApplicationInfo app : apps) {
final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan);
- final AppPreference pref = new AppPreference(getPrefContext());
+ final RestrictedAppPreference pref = new RestrictedAppPreference(getPrefContext());
pref.setKey(pkg);
pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label);
@@ -133,6 +133,8 @@
} else {
// Not auto approved, update summary according to notification backend.
pref.setSummary(getPreferenceSummary(pkg));
+ pref.checkEcmRestrictionAndSetDisabled(
+ android.Manifest.permission.MANAGE_NOTIFICATIONS, app.packageName);
}
pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment(
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 149c0d6..63b1dc9 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -18,6 +18,8 @@
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
+
import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.KeyguardManager;
@@ -37,6 +39,7 @@
import androidx.fragment.app.FragmentActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -112,23 +115,31 @@
private void promptToSetDeviceLock() {
Log.d(TAG, "Show prompt to set device lock before using private space feature");
- new AlertDialog.Builder(this)
- .setTitle(R.string.no_device_lock_title)
- .setMessage(R.string.no_device_lock_summary)
- .setPositiveButton(
- R.string.no_device_lock_action_label,
- (DialogInterface dialog, int which) -> {
- Log.d(TAG, "Start activity to set new device lock");
- mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD));
- })
- .setNegativeButton(
- R.string.no_device_lock_cancel,
- (DialogInterface dialog, int which) -> finish())
- .setOnCancelListener(
- (DialogInterface dialog) -> {
- finish();
- })
- .show();
+ if (android.multiuser.Flags.showSetScreenLockDialog()) {
+ Intent setScreenLockPromptIntent =
+ SetScreenLockDialogActivity
+ .createBaseIntent(LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS);
+ startActivity(setScreenLockPromptIntent);
+ finish();
+ } else {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.no_device_lock_title)
+ .setMessage(R.string.no_device_lock_summary)
+ .setPositiveButton(
+ R.string.no_device_lock_action_label,
+ (DialogInterface dialog, int which) -> {
+ Log.d(TAG, "Start activity to set new device lock");
+ mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD));
+ })
+ .setNegativeButton(
+ R.string.no_device_lock_cancel,
+ (DialogInterface dialog, int which) -> finish())
+ .setOnCancelListener(
+ (DialogInterface dialog) -> {
+ finish();
+ })
+ .show();
+ }
}
private KeyguardManager getKeyguardManager() {
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
index 7f7d8c5..1ed5959 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptions.kt
@@ -17,6 +17,7 @@
package com.android.settings.spa.app.appinfo
import android.app.AppOpsManager
+import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.UserManager
@@ -90,12 +91,18 @@
private fun ApplicationInfo.allowRestrictedSettings(context: Context, onSuccess: () -> Unit) {
AppInfoDashboardFragment.showLockScreen(context) {
- context.appOpsManager.setMode(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid,
- packageName,
- AppOpsManager.MODE_ALLOWED,
- )
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!!
+ manager.clearRestriction(packageName)
+ } else {
+ context.appOpsManager.setMode(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid,
+ packageName,
+ AppOpsManager.MODE_ALLOWED,
+ )
+ }
onSuccess()
val toastString = context.getString(
R.string.toast_allows_restricted_settings_successfully,
@@ -137,7 +144,7 @@
)
}
val shouldShowAccessRestrictedSettingsDeferred = async {
- shouldShowAccessRestrictedSettings(context.appOpsManager)
+ shouldShowAccessRestrictedSettings(context)
}
val isProfileOrDeviceOwner =
Utils.isProfileOrDeviceOwner(context.userManager, context.devicePolicyManager, packageName)
@@ -169,7 +176,14 @@
.filter { it.id != userId }
.any { packageManagers.isPackageInstalledAsUser(packageName, it.id) }
-private fun ApplicationInfo.shouldShowAccessRestrictedSettings(appOpsManager: AppOpsManager) =
- appOpsManager.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null
- ) == AppOpsManager.MODE_IGNORED
+private fun ApplicationInfo.shouldShowAccessRestrictedSettings(context: Context): Boolean {
+ return if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ val manager = context.getSystemService(EnhancedConfirmationManager::class.java)!!
+ manager.isClearRestrictionAllowed(packageName)
+ } else {
+ context.appOpsManager.noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid, packageName, null, null
+ ) == AppOpsManager.MODE_IGNORED
+ }
+}
diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java
index d5f0040..9f5fbc5 100644
--- a/src/com/android/settings/utils/ManagedServiceSettings.java
+++ b/src/com/android/settings/utils/ManagedServiceSettings.java
@@ -37,14 +37,14 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen;
-import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.widget.EmptyTextSettings;
+import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ServiceListing;
-import com.android.settingslib.widget.AppSwitchPreference;
+import com.android.settingslib.widget.TwoTargetPreference;
import java.util.List;
@@ -121,10 +121,12 @@
}
final CharSequence finalTitle = title;
final String summary = service.loadLabel(mPm).toString();
- final TwoStatePreference pref = new AppSwitchPreference(getPrefContext());
+ final RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(getPrefContext());
pref.setPersistent(false);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
UserHandle.getUserId(service.applicationInfo.uid)));
+ pref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM);
if (title != null && !title.equals(summary)) {
pref.setTitle(title);
pref.setSummary(summary);
@@ -150,6 +152,9 @@
}
});
pref.setKey(cn.flattenToString());
+ if (!pref.isChecked()) {
+ pref.checkEcmRestrictionAndSetDisabled(mConfig.permission, service.packageName);
+ }
screen.addPreference(pref);
}
highlightPreferenceIfNeeded();
diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java
index f93b935..c76a5de 100644
--- a/src/com/android/settings/widget/RestrictedAppPreference.java
+++ b/src/com/android/settings/widget/RestrictedAppPreference.java
@@ -21,6 +21,7 @@
import android.text.TextUtils;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
@@ -72,10 +73,18 @@
@Override
public void setEnabled(boolean enabled) {
- if (isDisabledByAdmin() && enabled) {
- return;
+ boolean changed = false;
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ changed = true;
}
- super.setEnabled(enabled);
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ changed = true;
+ }
+ if (!changed) {
+ super.setEnabled(enabled);
+ }
}
public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
@@ -88,6 +97,10 @@
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public void useAdminDisabledSummary(boolean useSummary) {
mHelper.useAdminDisabledSummary(useSummary);
}
@@ -112,4 +125,15 @@
public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
index c105d08..d88a83e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
@@ -38,6 +38,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.google.common.collect.ImmutableList;
@@ -57,7 +58,10 @@
import java.util.List;
/** Tests for {@link AccessibilityDetailsSettingsFragment}. */
-@Config(shadows = ShadowDevicePolicyManager.class)
+@Config(shadows = {
+ ShadowDevicePolicyManager.class,
+ ShadowRestrictedLockUtilsInternal.class
+})
@RunWith(RobolectricTestRunner.class)
public class AccessibilityDetailsSettingsFragmentTest {
private static final String PACKAGE_NAME = "com.foo.bar";
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index db6f43b..05e56ca 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -52,6 +52,7 @@
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -87,6 +88,7 @@
ShadowUserManager.class,
ShadowColorDisplayManager.class,
ShadowApplicationPackageManager.class,
+ ShadowRestrictedLockUtilsInternal.class,
})
public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
diff --git a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
index 99a78cf..bc9c1d8 100644
--- a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
@@ -31,9 +31,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import org.junit.Rule;
@@ -45,6 +50,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -53,6 +59,9 @@
/** Test for {@link RestrictedPreferenceHelper}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRestrictedLockUtilsInternal.class
+})
public class RestrictedPreferenceHelperTest {
private static final String PACKAGE_NAME = "com.android.test";
@@ -72,6 +81,11 @@
private AccessibilityShortcutInfo mShortcutInfo;
private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
@@ -86,6 +100,37 @@
}
@Test
+ @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
+ public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() {
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(
+ mServiceInfo.getResolveInfo().serviceInfo.packageName);
+ final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
+ singletonList(mServiceInfo));
+
+ final List<RestrictedPreference> preferenceList =
+ mHelper.createAccessibilityServicePreferenceList(infoList);
+ final RestrictedPreference preference = preferenceList.get(0);
+
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
+ public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() {
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+ final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
+ singletonList(mServiceInfo));
+
+ final List<RestrictedPreference> preferenceList =
+ mHelper.createAccessibilityServicePreferenceList(infoList);
+ final RestrictedPreference preference = preferenceList.get(0);
+
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ }
+
+ @Test
public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
setMockAccessibilityShortcutInfo(mShortcutInfo);
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
index e91c0fa..46b1910 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/premiumsms/PremiumSmsAccessTest.java
@@ -16,13 +16,35 @@
package com.android.settings.applications.specialaccess.premiumsms;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
import android.telephony.SmsManager;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.R;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.datausage.AppStateDataUsageBridge;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedDropDownPreference;
+import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
@@ -30,19 +52,28 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRestrictedLockUtilsInternal.class
+})
public class PremiumSmsAccessTest {
private FakeFeatureFactory mFeatureFactory;
private PremiumSmsAccess mFragment;
+ private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mFragment = new PremiumSmsAccess();
- mFragment.onAttach(RuntimeEnvironment.application);
+ mContext = RuntimeEnvironment.application;
+ mFragment.onAttach(mContext);
}
@Test
@@ -74,4 +105,89 @@
"app",
SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW);
}
+
+ @Test
+ public void onRebuildComplete_ecmRestricted_shouldBeDisabled() {
+ mFragment = spy(mFragment);
+ mContext = spy(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.preference_dropdown, null);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext));
+ doReturn(preferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(preferenceScreen).when(mFragment).getPreferenceScreen();
+ final String testPkg = "com.example.disabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg);
+
+ doAnswer((invocation) -> {
+ final RestrictedDropDownPreference preference = invocation.getArgument(0);
+ // Verify preference is disabled by ecm and the summary is changed accordingly.
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ assertThat(preference.getSummary().toString()).isEqualTo(
+ mContext.getString(
+ com.android.settingslib.R.string.disabled_by_app_ops_text));
+ preference.onBindViewHolder(holder);
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is launched
+ verify(mContext).startActivity(any());
+
+ return null;
+ }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+
+ mFragment.onRebuildComplete(createAppEntries(testPkg));
+ verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+ }
+
+ @Test
+ public void onRebuildComplete_ecmNotRestricted_notDisabled() {
+ mFragment = spy(mFragment);
+ mContext = spy(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.preference_dropdown, null);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = spy(preferenceManager.createPreferenceScreen(mContext));
+ doReturn(preferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(preferenceScreen).when(mFragment).getPreferenceScreen();
+ final String testPkg = "com.example.enabled";
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+
+
+ doAnswer((invocation) -> {
+ final RestrictedDropDownPreference preference = invocation.getArgument(0);
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ assertThat(preference.getSummary().toString()).isEqualTo("");
+ preference.onBindViewHolder(holder);
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is not launched
+ verify(mContext, never()).startActivity(any());
+
+ return null;
+ }).when(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+
+ mFragment.onRebuildComplete(createAppEntries(testPkg));
+ verify(preferenceScreen).addPreference(any(RestrictedDropDownPreference.class));
+ }
+
+ private ArrayList<ApplicationsState.AppEntry> createAppEntries(String... packageNames) {
+ final ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
+ for (int i = 0; i < packageNames.length; ++i) {
+ final ApplicationInfo info = new ApplicationInfo();
+ info.packageName = packageNames[i];
+ info.uid = Process.FIRST_APPLICATION_UID + i;
+ info.sourceDir = info.packageName;
+ final ApplicationsState.AppEntry appEntry =
+ spy(new ApplicationsState.AppEntry(mContext, info, i));
+ appEntry.extraInfo = new AppStateDataUsageBridge
+ .DataUsageState(false, false);
+ doNothing().when(appEntry).ensureLabel(any(Context.class));
+ ReflectionHelpers.setField(appEntry, "info", info);
+ appEntries.add(appEntry);
+ }
+ return appEntries;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
index beb7a9f..a56c34c 100644
--- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
@@ -43,6 +43,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -196,6 +197,8 @@
final Intent testIntent = new Intent();
// Set the challenge token so the confirm screen will not be shown
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
+ testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_SETTINGS);
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
null /* Simulate no posture intent */);
@@ -220,6 +223,8 @@
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
testIntent.putExtra(EXTRA_KEY_NEXT_LAUNCHED, false);
testIntent.putExtra(EXTRA_LAUNCHED_POSTURE_GUIDANCE, false);
+ testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_SETTINGS);
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
testIntent);
@@ -641,4 +646,14 @@
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
+
+ @Test
+ public void testFaceEnrollIntroduction_forwardsEnrollOptions() {
+ setupActivity();
+ final Intent intent = mActivity.getEnrollingIntent();
+
+ assertThat(intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1))
+ .isEqualTo(FaceEnrollOptions.ENROLL_REASON_SETTINGS);
+ }
+
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 4de369e..91707cd 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -669,7 +669,8 @@
any(CancellationSignal.class),
anyInt(),
callbackCaptor.capture(),
- eq(FingerprintManager.ENROLL_ENROLL));
+ eq(FingerprintManager.ENROLL_ENROLL),
+ any());
return callbackCaptor.getValue();
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
index 4d13bb6..68395b2 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -227,7 +227,8 @@
any(CancellationSignal.class),
anyInt(),
callbackCaptor.capture(),
- eq(FingerprintManager.ENROLL_FIND_SENSOR));
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any());
return callbackCaptor.getValue();
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
index 3eba91c..89e70d2 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
@@ -40,6 +40,7 @@
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -52,6 +53,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -289,6 +291,18 @@
assertThat(mFingerprintEnrollIntroduction.shouldFinishWhenBackgrounded()).isEqualTo(true);
}
+ @Test
+ public void testFingerprintEnrollIntroduction_forwardsEnrollOptions() {
+ final Intent intent = newTokenOnlyIntent();
+ intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
+ FingerprintEnrollOptions.ENROLL_REASON_SETTINGS);
+ setupFingerprintEnrollIntroWith(intent);
+
+ final Intent enrollingIntent = mFingerprintEnrollIntroduction.getEnrollingIntent();
+ assertThat(enrollingIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1))
+ .isEqualTo(FingerprintEnrollOptions.ENROLL_REASON_SUW);
+ }
+
private Intent newTokenOnlyIntent() {
return new Intent()
.putExtra(EXTRA_KEY_CHALLENGE_TOKEN, new byte[] { 1 });
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
new file mode 100644
index 0000000..d82dcc4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.bluetooth;
+
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
+import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothAutoOnPreferenceControllerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private BluetoothAutoOnPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mContentResolver = mContext.getContentResolver();
+ mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY);
+ }
+
+ @Test
+ public void getAvailability_valueUnset_returnUnsupported() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailability_valueSet_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_valueEnabled_returnTrue() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ assertThat(mController.isChecked()).isEqualTo(true);
+ }
+
+ @Test
+ public void setChecked_returnTrue() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
+
+ mController.setChecked(true);
+ assertThat(mController.isChecked()).isEqualTo(true);
+ }
+
+ @Test
+ public void setChecked_returnFalse() {
+ Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
+
+ mController.setChecked(false);
+ assertThat(mController.isChecked()).isEqualTo(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
index 8aa2e5a..ce47052 100644
--- a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -163,6 +164,81 @@
mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2));
}
+ @Test
+ public void onRebuildComplete_ecmRestricted_shouldBeDisabled() {
+ mFragment = spy(new UnrestrictedDataAccess());
+ mContext = spy(mContext);
+ doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
+ mController.setParentFragment(mFragment);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+ doReturn(mPreferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
+ doReturn(0).when(mPreferenceScreen).getPreferenceCount();
+ final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+ ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend);
+ ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen);
+
+ final String testPkg = "com.example.disabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(testPkg);
+
+ doAnswer((invocation) -> {
+ final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
+ // Verify preference is disabled by ecm and the summary is changed accordingly.
+ assertThat(preference.isDisabledByEcm()).isTrue();
+ assertThat(preference.getSummary().toString()).isEqualTo(
+ mContext.getString(
+ com.android.settingslib.R.string.disabled_by_app_ops_text));
+ assertThat(preference.isChecked()).isFalse();
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is launched
+ assertThat(preference.isChecked()).isFalse();
+ verify(mContext).startActivity(any());
+
+ return null;
+ }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+
+ mController.onRebuildComplete(createAppEntries(testPkg));
+ verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+ }
+
+ @Test
+ public void onRebuildComplete_ecmNotRestricted_notDisabled() {
+ mFragment = spy(new UnrestrictedDataAccess());
+ mContext = spy(mContext);
+ doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean());
+ mController.setParentFragment(mFragment);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+ doReturn(mPreferenceManager).when(mFragment).getPreferenceManager();
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
+ doReturn(0).when(mPreferenceScreen).getPreferenceCount();
+ final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
+ ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend);
+ ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen);
+
+ final String testPkg = "com.example.enabled";
+ doNothing().when(mContext).startActivity(any());
+ ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
+
+ doAnswer((invocation) -> {
+ final UnrestrictedDataAccessPreference preference = invocation.getArgument(0);
+ assertThat(preference.isDisabledByEcm()).isFalse();
+ assertThat(preference.getSummary()).isEqualTo("");
+ assertThat(preference.isChecked()).isFalse();
+ preference.performClick();
+ // Verify that when the preference is clicked, ecm details intent is not launched
+ assertThat(preference.isChecked()).isTrue();
+ verify(mContext, never()).startActivity(any());
+
+ return null;
+ }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+
+ mController.onRebuildComplete(createAppEntries(testPkg));
+ verify(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class));
+ }
+
private ArrayList<AppEntry> createAppEntries(String... packageNames) {
final ArrayList<AppEntry> appEntries = new ArrayList<>();
for (int i = 0; i < packageNames.length; ++i) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index 0339f57..e99c4e0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -64,7 +64,7 @@
private static final String STATUS_CHARGING_NO_TIME = "50% - charging";
private static final String STATUS_CHARGING_TIME = "50% - 0 min left until full";
private static final String STATUS_NOT_CHARGING = "Not charging";
- private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging optimized";
+ private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging";
private static final String STATUS_CHARGING_PAUSED = "50% - Charging optimized";
private static final long REMAINING_TIME_NULL = -1;
private static final long REMAINING_TIME = 2;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
index 4efd850..a605ee3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
@@ -82,7 +82,7 @@
@Test
public void getIcon_showIcon() {
assertThat(mBatteryDefenderTip.getIconId())
- .isEqualTo(R.drawable.ic_battery_status_good_theme);
+ .isEqualTo(R.drawable.ic_battery_defender_tip_shield);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
index 635343e..c38e36a 100644
--- a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
+++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
@@ -2,53 +2,56 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.content.Context;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.testing.FragmentScenario;
+import androidx.test.core.app.ApplicationProvider;
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.overlay.SurveyFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
-import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SurveyMixinTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final String FAKE_KEY = "fake_key";
-
- private Context mContext;
private SurveyFeatureProvider mProvider;
- @Mock
- private InstrumentedPreferenceFragment mFragment;
@Before
public void setUp() {
// set up the fakefeature factory to mock out the survey provider
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext);
+ mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(
+ ApplicationProvider.getApplicationContext());
}
- @Ignore("b/314929422")
@Test
public void onResume_noActionIfActivityDoesNotExist() {
- // Pretend we are an activity that is starting up
- FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class);
- when(mFragment.getActivity()).thenReturn(null);
- SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY);
+ // Initialize a fragment without associating with an activity
+ Fragment fragment = new Fragment();
+ SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY);
mixin.onResume();
verify(mProvider, times(0)).sendActivityIfAvailable(FAKE_KEY);
}
+
+ @Test
+ public void onResume_sendActivityWhenSurveyFeatureExists() {
+ try (var fragmentScenario = FragmentScenario.launch(Fragment.class)) {
+ fragmentScenario.onFragment(fragment -> {
+ SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY);
+ mixin.onResume();
+ });
+ }
+ // Verify one send activity action is attempted
+ verify(mProvider).sendActivityIfAvailable(FAKE_KEY);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
index 5989d49..e03134b 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRestrictedLockUtilsInternal.java
@@ -15,9 +15,11 @@
*/
package com.android.settings.testutils.shadow;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -38,6 +40,8 @@
private static DevicePolicyManager sDevicePolicyManager;
private static String[] sDisabledTypes;
private static int sKeyguardDisabledFeatures;
+ private static String[] sEcmRestrictedPkgs;
+ private static boolean sAccessibilityServiceRestricted;
@Resetter
public static void reset() {
@@ -47,6 +51,8 @@
sDisabledTypes = new String[0];
sMaximumTimeToLockIsSet = false;
sMteOverridden = false;
+ sEcmRestrictedPkgs = new String[0];
+ sAccessibilityServiceRestricted = false;
}
@Implementation
@@ -108,10 +114,35 @@
return sMteOverridden ? new EnforcedAdmin() : null;
}
+ public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
+ String packageName, int userId) {
+ return sAccessibilityServiceRestricted ? new EnforcedAdmin() : null;
+ }
+
+ @Implementation
+ public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ if (ArrayUtils.contains(sEcmRestrictedPkgs, packageName)) {
+ return new Intent();
+ }
+
+ return null;
+ }
+
+ @Implementation
+ public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ return false;
+ }
+
public static void setRestricted(boolean restricted) {
sIsRestricted = restricted;
}
+ public static void setEcmRestrictedPkgs(String... pkgs) {
+ sEcmRestrictedPkgs = pkgs;
+ }
+
public static void setRestrictedPkgs(String... pkgs) {
sRestrictedPkgs = pkgs;
}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
index ec3b754..4155318 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
@@ -58,7 +58,7 @@
@Test
fun getApnDataFromUri() {
// mock out resources and the feature provider
- val cursor = MatrixCursor(sProjection)
+ val cursor = MatrixCursor(Projection)
cursor.addRow(
arrayOf<Any>(
0,
@@ -82,7 +82,7 @@
1,
)
)
- whenever(contentResolver.query(uri, sProjection, null, null, null)).thenReturn(cursor)
+ whenever(contentResolver.query(uri, Projection, null, null, null)).thenReturn(cursor)
val apnData = getApnDataFromUri(uri, context)
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
index 7103516..9e7ec9b 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInfoSettingsMoreOptionsTest.kt
@@ -19,11 +19,16 @@
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
+import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.os.UserManager
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed
@@ -61,6 +66,9 @@
@get:Rule
val composeTestRule = createComposeRule()
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private lateinit var mockSession: MockitoSession
@Spy
@@ -82,6 +90,9 @@
private lateinit var appOpsManager: AppOpsManager
@Mock
+ private lateinit var enhancedConfirmationManager: EnhancedConfirmationManager
+
+ @Mock
private lateinit var keyguardManager: KeyguardManager
@Spy
@@ -103,6 +114,8 @@
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
whenever(context.appOpsManager).thenReturn(appOpsManager)
whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager)
+ whenever(context.getSystemService(EnhancedConfirmationManager::class.java))
+ .thenReturn(enhancedConfirmationManager)
whenever(keyguardManager.isKeyguardSecure).thenReturn(false)
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(false)
@@ -158,7 +171,9 @@
}
@Test
- fun shouldShowAccessRestrictedSettings() {
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ fun shouldShowAccessRestrictedSettings_appOp() {
whenever(
appOpsManager.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, UID, PACKAGE_NAME, null, null
@@ -186,6 +201,31 @@
)
}
+ @Test
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+ android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ fun shouldShowAccessRestrictedSettings() {
+ whenever(
+ enhancedConfirmationManager.isClearRestrictionAllowed(PACKAGE_NAME)
+ ).thenReturn(true)
+ val app = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ uid = UID
+ }
+
+ setContent(app)
+ composeTestRule.onRoot().performClick()
+
+ composeTestRule.waitUntilExists(
+ hasText(context.getString(R.string.app_restricted_settings_lockscreen_title))
+ )
+ composeTestRule
+ .onNodeWithText(context
+ .getString(R.string.app_restricted_settings_lockscreen_title))
+ .performClick()
+ verify(enhancedConfirmationManager).clearRestriction(PACKAGE_NAME)
+ }
+
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
diff --git a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
index 8751471..8eadd9d 100644
--- a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
+++ b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt
@@ -18,9 +18,11 @@
import android.os.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
+import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
@@ -77,15 +79,18 @@
Thread.sleep(1000)
device.assertHasTexts(listOf(SET_LOCK_BUTTON,CANCEL_TEXT))
device.clickObject(By.text(SET_LOCK_BUTTON))
- device.assertHasTexts(listOf(LOCK_SCREEN_TITLE))
+ Thread.sleep(1000)
+ device.assertHasTexts(listOf(PATTERN_TEXT, PIN_TEXT, PASSWORD_TEXT))
}
private companion object {
// Items we really want to always show
- val PRIVATE_SPACE_SETTING = "Private Space"
+ val PRIVATE_SPACE_SETTING = "Private space"
const val SET_LOCK_BUTTON = "Set screen lock"
val CANCEL_TEXT = "Cancel"
val DIALOG_TITLE = "Set a screen lock"
- val LOCK_SCREEN_TITLE = "Choose screen lock"
+ val PATTERN_TEXT = "Pattern"
+ val PIN_TEXT = "PIN"
+ val PASSWORD_TEXT = "Password"
}
}
diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
index fa5af6d..eb23685 100644
--- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
@@ -36,6 +36,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
@@ -45,6 +48,7 @@
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.RestrictedSwitchPreference;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -58,6 +62,8 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
@@ -122,6 +128,7 @@
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void updateState_checked() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
@@ -136,19 +143,26 @@
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_appOpsDisabled() {
- when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
- AppOpsManager.MODE_ERRORED);
+ Assert.assertFalse(android.security.Flags.extendEcmToAllSettings());
+ when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
+ mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
mController.updateState(pref);
+
+ verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString());
assertThat(pref.isEnabled()).isFalse();
}
@Test
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_serviceAlreadyEnabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ERRORED);
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
index 9190d0a..1701eaa 100644
--- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.Intent;
import android.hardware.face.Face;
import android.hardware.face.FaceEnrollCell;
import android.hardware.face.FaceEnrollStages;
@@ -93,7 +94,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -101,7 +102,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -121,7 +123,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -129,7 +131,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -142,7 +145,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES);
+ DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -150,7 +153,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
- eq(false));
+ eq(false),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
@@ -163,7 +167,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -171,7 +175,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -191,7 +196,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -199,7 +204,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -212,7 +218,7 @@
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
- DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT);
+ DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
@@ -220,7 +226,8 @@
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
- eq(DEBUG_CONSENT));
+ eq(DEBUG_CONSENT),
+ any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
index 62435b4..6218efb 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
@@ -78,13 +79,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -101,13 +103,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
@@ -120,13 +123,14 @@
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
- mEnrollmentCallback, ENROLL_REASON);
+ mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
- eq(ENROLL_REASON));
+ eq(ENROLL_REASON),
+ any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
index 794cfec..f7256b9 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt
@@ -44,16 +44,24 @@
@RunWith(AndroidJUnit4::class)
class SfpsEnrollmentFeatureImplTest {
+ private val STAGE_0 = 0
+ private val STAGE_1 = 1
+ private val STAGE_2 = 2
+ private val STAGE_3 = 3
+ private val STAGE_4 = 4
+
+ private val THRESHOLD_0 = 0f
+ private val THRESHOLD_1 = .36f
+ private val THRESHOLD_2 = .52f
+ private val THRESHOLD_3 = .76f
+ private val THRESHOLD_4 = 1f
+
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
- private val settingsPackageName = "com.android.settings"
-
- private lateinit var settingsContext: Context
-
@Mock
private lateinit var mockFingerprintManager: FingerprintManager
@@ -61,31 +69,34 @@
@Before
fun setUp() {
- assertThat(mSfpsEnrollmentFeatureImpl).isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
whenever(context.getSystemService(FingerprintManager::class.java))
.thenReturn(mockFingerprintManager)
- doReturn(0f).`when`(mockFingerprintManager).getEnrollStageThreshold(0)
- doReturn(0.36f).`when`(mockFingerprintManager).getEnrollStageThreshold(1)
- doReturn(0.52f).`when`(mockFingerprintManager).getEnrollStageThreshold(2)
- doReturn(0.76f).`when`(mockFingerprintManager).getEnrollStageThreshold(3)
- doReturn(1f).`when`(mockFingerprintManager).getEnrollStageThreshold(4)
- settingsContext = context.createPackageContext(settingsPackageName, 0)
+ doReturn(THRESHOLD_0).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_0)
+ doReturn(THRESHOLD_1).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_1)
+ doReturn(THRESHOLD_2).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_2)
+ doReturn(THRESHOLD_3).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_3)
+ doReturn(THRESHOLD_4).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_4)
}
@Test
fun testGetEnrollStageThreshold() {
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 0)).isEqualTo(0f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 1)).isEqualTo(0.36f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 2)).isEqualTo(0.52f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 3)).isEqualTo(0.76f)
- assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 4)).isEqualTo(1f)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_0))
+ .isEqualTo(THRESHOLD_0)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_1))
+ .isEqualTo(THRESHOLD_1)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_2))
+ .isEqualTo(THRESHOLD_2)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_3))
+ .isEqualTo(THRESHOLD_3)
+ assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_4))
+ .isEqualTo(THRESHOLD_4)
}
@Test
fun testGetHelpAnimator() {
val mockView: View = mock(View::class.java)
val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView)
- assertThat(animator.duration).isEqualTo(550)
+ assertThat(animator.duration).isEqualTo(SfpsEnrollmentFeatureImpl.HELP_ANIMATOR_DURATION)
}
@Test
@@ -115,42 +126,27 @@
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_fingerprint_enroll_repeat_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_fingerprint_enroll_repeat_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_finger_center_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_finger_center_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_fingertip_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_fingertip_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_left_edge_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_left_edge_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE)
).isEqualTo(
- settingsContext.resources.getIdentifier(
- "security_settings_sfps_enroll_right_edge_title",
- type,
- settingsPackageName)
+ getSettingsResourcesId(type, "security_settings_sfps_enroll_right_edge_title")
)
}
@@ -159,43 +155,22 @@
val type = "raw"
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_no_animation",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_no_animation"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_pad_center",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_pad_center"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_tip",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_tip"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_left_edge",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_left_edge"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE)
- ).isEqualTo(
- settingsContext.resources.getIdentifier(
- "sfps_lottie_right_edge",
- type,
- settingsPackageName)
- )
+ ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_right_edge"))
+ }
+
+ private fun getSettingsResourcesId(type: String, name: String) : Int {
+ return context.resources.getIdentifier(name, type, context.packageName)
}
}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
index 418db04..2c7afa6 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
@@ -98,7 +98,7 @@
mCallbackWrapper.mValue = invocation.getArgument(3);
return null;
}).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt());
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt(), any());
}
@Test
@@ -112,7 +112,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
}
@@ -127,7 +127,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
}
@@ -146,7 +146,7 @@
assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue).isNotNull();
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue();
@@ -158,7 +158,7 @@
// Shall not use the same MessageDisplayController
verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class),
- eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+ eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
assertThat(mCallbackWrapper.mValue).isNotNull();
assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue);
}
@@ -170,7 +170,8 @@
assertThat(ret).isNull();
verify(mFingerprintUpdater, never()).enroll(any(byte[].class),
- any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt());
+ any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt(),
+ any());
}
@Test
diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
index c284a6f..2b4bff7 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.hardware.fingerprint.Fingerprint
+import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.CryptoObject
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT
@@ -99,6 +100,7 @@
gateKeeperPasswordProvider,
pressToAuthInteractor,
Default,
+ Intent(),
)
}
@@ -312,7 +314,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentProgress(1)
runCurrent()
@@ -336,7 +339,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentHelp(-1, "help")
runCurrent()
@@ -360,7 +364,8 @@
any(CancellationSignal::class.java),
anyInt(),
capture(enrollCallback),
- eq(FingerprintManager.ENROLL_FIND_SENSOR)
+ eq(FingerprintManager.ENROLL_FIND_SENSOR),
+ any(FingerprintEnrollOptions::class.java),
)
enrollCallback.value.onEnrollmentError(-1, "error")
runCurrent()