diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6d2c636ccf7..1f2e4aba027 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -123,7 +123,8 @@ + + + + + diff --git a/res/drawable/ic_settings_install.xml b/res/drawable/ic_settings_install.xml index 5bd5e300eae..eefecc242a4 100644 --- a/res/drawable/ic_settings_install.xml +++ b/res/drawable/ic_settings_install.xml @@ -18,7 +18,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> diff --git a/res/drawable/quickly_open_camera.xml b/res/drawable/quickly_open_camera.xml deleted file mode 100644 index dcbf9f4dc68..00000000000 --- a/res/drawable/quickly_open_camera.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml index 8b0352928ca..ea57f4acbc9 100644 --- a/res/layout/face_enroll_introduction.xml +++ b/res/layout/face_enroll_introduction.xml @@ -64,7 +64,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/BiometricEnrollIntroTitle" - android:text="@string/security_settings_face_enroll_introduction_info_title" /> + android:text="@string/security_settings_face_enroll_introduction_info_title_en" /> + android:text="@string/security_settings_face_enroll_introduction_how_title_en" /> + android:entries="@array/wifi_privacy_entries_extended"/> + + + + + diff --git a/res/mipmap-anydpi/ic_launcher_round.xml b/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 00000000000..5c84730caa7 --- /dev/null +++ b/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/raw/lottie_quickly_open_camera.json b/res/raw/lottie_quickly_open_camera.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 42d60eee989..ba65ea01cb1 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -39,6 +39,94 @@ All + + + @string/bluetooth_timeout_summary_never + @string/bluetooth_timeout_summary_15secs + @string/bluetooth_timeout_summary_30secs + @string/bluetooth_timeout_summary_1min + @string/bluetooth_timeout_summary_2mins + @string/bluetooth_timeout_summary_5mins + @string/bluetooth_timeout_summary_10mins + @string/bluetooth_timeout_summary_30mins + @string/bluetooth_timeout_summary_1hour + @string/bluetooth_timeout_summary_2hours + @string/bluetooth_timeout_summary_4hours + @string/bluetooth_timeout_summary_8hours + + + + + + 0 + + 15000 + + 30000 + + 60000 + + 120000 + + 300000 + + 600000 + + 1800000 + + 3600000 + + 7200000 + + 14400000 + + 28800000 + + + + + @string/wifi_timeout_summary_never + @string/wifi_timeout_summary_15secs + @string/wifi_timeout_summary_30secs + @string/wifi_timeout_summary_1min + @string/wifi_timeout_summary_2mins + @string/wifi_timeout_summary_5mins + @string/wifi_timeout_summary_10mins + @string/wifi_timeout_summary_30mins + @string/wifi_timeout_summary_1hour + @string/wifi_timeout_summary_2hours + @string/wifi_timeout_summary_4hours + @string/wifi_timeout_summary_8hours + + + + + + 0 + + 15000 + + 30000 + + 60000 + + 120000 + + 300000 + + 600000 + + 1800000 + + 3600000 + + 7200000 + + 14400000 + + 28800000 + + 15 seconds @@ -136,6 +224,44 @@ 30 minutes + + Deny new USB peripherals + Allow new USB peripherals when unlocked + Allow new USB peripherals + + + + + + enabled + + dynamic + + disabled + + + + GrapheneOS + Standard (Google) + Disabled + + + + 0 + 1 + 2 + + + + GrapheneOS proxy + Standard (Google) + + + + 0 + 1 + + @@ -1293,6 +1419,12 @@ Treat as unmetered + + Use per-connection randomized MAC (default) + Use per-network randomized MAC + Use device MAC + + Use randomized MAC (default) Use device MAC @@ -1310,6 +1442,7 @@ + 100 1 0 diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 307871716aa..4b59dd9768f 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -68,6 +68,8 @@ + + diff --git a/res/values/config.xml b/res/values/config.xml index 4aa41425ce6..59417592b99 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -45,14 +45,14 @@ com.android.settings.overlay.FeatureFactoryImpl - com.android.settings - com.android.settings.Settings$WallpaperSettingsActivity + com.android.wallpaper + com.android.wallpaper.picker.CategoryPickerActivity - + com.android.customization.picker.CustomizationPickerActivity - + android.intent.action.MAIN - + android.intent.action.MAIN com.android.wallpaper.LAUNCH_SOURCE @@ -67,7 +67,7 @@ - false + true false @@ -81,6 +81,7 @@ com.example.package.first/com.example.class.FirstService com.example.package.second/com.example.class.SecondService --> + com.android.talkback/com.google.android.marvin.talkback.TalkBackService @@ -221,7 +222,7 @@ Whether or not the homepage should be powered by legacy suggestion (versus contextual cards) Default to true as not all devices support contextual cards. --> - true + false false @@ -431,7 +432,7 @@ true - true + false true @@ -473,7 +474,7 @@ true - content://com.google.android.gms.nearby.fastpair/device_status_list_item + diff --git a/res/values/ic_launcher_background.xml b/res/values/ic_launcher_background.xml new file mode 100644 index 00000000000..f42ada656ee --- /dev/null +++ b/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + diff --git a/res/values/strings.xml b/res/values/strings.xml index 28b35b3fcfc..caeab78fd4e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -27,6 +27,45 @@ Turn on + + Bluetooth timeout + + + Bluetooth will turn off after %1$s if no devices connected + Do not automatically turn off Bluetooth + Never + 15 seconds + 30 seconds + 1 minute + 2 minutes + 5 minutes + 10 minutes + 30 minutes + 1 hour + 2 hours + 4 hours + 8 hours + + + Turn off Wi-Fi automatically + + + Automatically turn off Wi-Fi after a set amount of time. + Wi-Fi will turn off after %1$s if no network connected + Disabled + Never + 15 seconds + 30 seconds + 1 minute + 2 minutes + 5 minutes + 10 minutes + 30 minutes + 1 hour + 2 hours + 4 hours + 8 hours + Unknown @@ -871,6 +910,18 @@ Use your face to unlock your phone, authorize purchases, or sign in to apps + + Keep in mind + How it works + + Glasses or lightly tinted sunglasses are OK. + Looking at the phone can unlock it even when you don’t intend to. Your phone can also be unlocked by someone who looks a lot like you, like an identical sibling, or if someone holds it up to your face. + Using a face to unlock the phone may be less secure than a strong pattern or PIN. + Face Unlock can require your eyes to be open to unlock the phone or verify it’s you. You can turn this option on at any time in Settings. + Face Unlock creates a unique model of your face to verify it’s you. To create this face model during setup, you will take images of your face from different angles.\n\nWhen you use Face Unlock, images are used to update your face model. Images used to create your face model are not stored, but the face model is stored securely on your phone and never leaves the phone. All processing occurs securely on your phone. + You’re in control + You can delete your face model or turn off Face Unlock at any time in Settings. Face models are stored on the phone until you delete them.\n\nLearn more at g.co/pixel/faceunlock. + @@ -3201,6 +3252,7 @@ Smooth Display Automatically raises the refresh rate from 60 to 90 Hz for some content. Increases battery usage. + Automatically raises the refresh rate from 60 to %s Hz for some content. Increases battery usage. Force peak refresh rate @@ -3235,6 +3287,10 @@ Camera access is required for Face Detection. Tap to manage permissions for Device Personalization Services Manage permissions + + Increase touch sensitivity + + Improves touch when using screen protectors Night Light @@ -3533,6 +3589,8 @@ Baseband version + Bootloader version + Kernel version Build number @@ -3549,6 +3607,8 @@ Status of the battery, network, and other information Phone number, signal, etc. + + Hardware SKU Storage @@ -4171,9 +4231,9 @@ Erase all data (factory reset) - "This will erase all data from your tablet\u2019s internal storage, including:\n\n
  • Your Google Account
  • \n
  • System and app data and settings
  • \n
  • Downloaded apps
  • "
    + "This will erase all data from your tablet\u2019s internal storage, including:\n\n
  • System and app data and settings
  • \n
  • Downloaded apps
  • "
    - "This will erase all data from your phone\u2019s internal storage, including:\n\n
  • Your Google Account
  • \n
  • System and app data and settings
  • \n
  • Downloaded apps
  • "
    + "This will erase all data from your phone\u2019s internal storage, including:\n\n
  • System and app data and settings
  • \n
  • Downloaded apps
  • "
    "\n\nYou are currently signed into the following accounts:\n" @@ -7970,6 +8030,10 @@ Turn on phone calls Turn on phone calls & SMS + + Disallow installing apps + + Disallow installing apps from third party sources Delete user @@ -13026,6 +13090,8 @@ Preferred network mode: CDMA/EvDo/GSM/WCDMA Preferred network mode: LTE + + Preferred network mode: LTE only Preferred network mode: GSM/WCDMA/LTE @@ -13091,8 +13157,12 @@ 4G LTE (recommended) + + LTE only 4G (recommended) + + 4G only 3G @@ -14457,4 +14527,38 @@ QR code isn\u0027t a valid format + + USB accessories + Control support for USB peripherals such as input (mice, keyboards, joysticks) and storage devices. + Enable secure app spawning + Launch apps in a more secure way than Android which takes slightly longer and increases memory usage by app processes. + Enable native code debugging + Generate useful logs / bug reports from crashes and permit debugging native code. + Allow fingerprint unlocking + Allow fingerprints to unlock the screen lock. If this is disabled, fingerprints can still be used in apps. + Internet connectivity check + HTTP endpoints to use for performing internet connectivity checks. + Send notifications to current user + Your lock screen notifications will be forwarded to the current user if you are active in the background. Only the user\'s name, the app\'s name, and the time received will be shown. + + PSDS server + Server used for providing satellite information to assist location + + Sandboxed Google Play + Sandboxed Google Play (work profile) + + Charge other devices by placing them on the back of your phone + Battery share + + Storage Scopes + + Allow access to Android/obb folder + Required for installation of some large apps (mostly games) that use OBB expansion files + + Exploit protection compatibility mode + Improve compatibility with misbehaving apps by using Android\'s standard address space size and memory allocator + Configure hardening + + Installed: %1$s + Updated: %1$s diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml new file mode 100644 index 00000000000..68ace5607ad --- /dev/null +++ b/res/values/strings_ext.xml @@ -0,0 +1,39 @@ + + + + Screen lock camera access + Allow camera access when the device is locked + + Allow Sensors permission to apps by default + Sensors is a non-standard permission, apps may malfunction if it’s denied. + A permission prompt will be shown when an app tries to access sensors. Note that some apps may need to be manually restarted after allowing the Sensors permission. + + Auto reboot + Automatically reboot the device if it hasn\'t been unlocked within the selected duration of time + + Save screenshot timestamp to EXIF + Enables adding a timestamp to screenshot EXIF metadata + + Secure User Plane Location (SUPL) + A-GNSS (assisted satellite geolocation) based on nearby cell towers + + Enabled; GrapheneOS proxy + Enabled; standard server + Disabled + + PIN scrambling + Controls PIN scrambling option when inputting PIN on screen lock. + + Enable privileged eSIM management + Requires sandboxed Google Play installation + + Attestation key provisioning server + Server used for provisioning hardware-based attestation keys + Enabled; GrapheneOS proxy + Enabled; standard server + Disabled + + Clipboard auto clear + Automatically clear the clipboard after a selected duration of time + + diff --git a/res/xml/app_info_settings.xml b/res/xml/app_info_settings.xml index 2bb05d000ae..89252b369e7 100644 --- a/res/xml/app_info_settings.xml +++ b/res/xml/app_info_settings.xml @@ -59,6 +59,11 @@ settings:summaryLineCount="1" settings:controller="com.android.settings.applications.appinfo.AppPermissionPreferenceController" /> + + + + + + + + diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index a0e0a1f26d6..ded58ebd419 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -47,6 +47,14 @@ settings:useAdminDisabledSummary="true" settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/> + + + + + app:lottie_rawRes="@raw/lottie_quickly_open_camera"/> - - - + + + + + + diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index fe87efd73d7..bb88acb197b 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -66,6 +66,12 @@ android:title="@string/location_services_preference_title" settings:controller="com.android.settings.location.LocationServicesPreferenceController"/> + + + + + + diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 9e86bf52b3d..1d922e13edc 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -64,6 +64,12 @@ android:summary="@string/battery_percentage_description" settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" /> + + + + + + + + + + - - - @@ -55,6 +49,45 @@ android:summary="@string/summary_placeholder" settings:keywords="@string/keywords_face_settings" /> + + + + + + + + + + + + + + diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml index 8c82b671688..54d8d93b514 100644 --- a/res/xml/top_level_settings.xml +++ b/res/xml/top_level_settings.xml @@ -18,7 +18,8 @@ + android:key="top_level_settings" + android:title="Settings"> + + + + diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml index 2ab7b6a28fe..a682e2db92d 100644 --- a/res/xml/wifi_configure_settings.xml +++ b/res/xml/wifi_configure_settings.xml @@ -26,6 +26,14 @@ android:summary="@string/wifi_wakeup_summary" settings:controller="com.android.settings.wifi.WifiWakeupPreferenceController"/> + + getEnabledPackageAllowlist() { if (mPm.getWellbeingPackageName() != null) { keepEnabledPackages.add(mPm.getWellbeingPackageName()); } + + keepEnabledPackages.add("com.android.inputmethod.latin"); + keepEnabledPackages.add("app.vanadium.webview"); + keepEnabledPackages.add("app.grapheneos.camera"); + keepEnabledPackages.add(com.android.internal.gmscompat.GmsCompatApp.PKG_NAME); + keepEnabledPackages.add(com.android.internal.gmscompat.GmsCompatApp.PKG_NAME + ".config"); + // handles firmware updates of embedded secure element that is used for eSIM, NFC, Felica etc + keepEnabledPackages.add(GoogleEuicc.EUICC_SUPPORT_PIXEL_PKG_NAME); + return keepEnabledPackages; } diff --git a/src/com/android/settings/applications/GmsCompatAppController.java b/src/com/android/settings/applications/GmsCompatAppController.java new file mode 100644 index 00000000000..ff8232d3601 --- /dev/null +++ b/src/com/android/settings/applications/GmsCompatAppController.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 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.applications; + +import android.app.compat.gms.GmsCompat; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.UserHandle; +import android.text.TextUtils; + +import androidx.preference.Preference; + +import com.android.internal.gmscompat.GmsCompatApp; +import com.android.internal.gmscompat.GmsInfo; +import com.android.settings.core.BasePreferenceController; + +public class GmsCompatAppController extends BasePreferenceController { + private final Context context; + + public GmsCompatAppController(Context context, String key) { + super(context, key); + this.context = context; + } + + @Override + public int getAvailabilityStatus() { + UserHandle workProfile = getWorkProfileUser(); + int userId = workProfile != null ? + workProfile.getIdentifier() : + UserHandle.myUserId(); + + return GmsCompat.isGmsApp(GmsInfo.PACKAGE_GMS_CORE, userId) ? + AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return false; + } + Intent intent = new Intent(GmsCompatApp.PKG_NAME + ".SETTINGS_LINK"); + intent.setPackage(GmsCompatApp.PKG_NAME); + + UserHandle workProfile = getWorkProfileUser(); + if (workProfile != null) { + context.startActivityAsUser(intent, workProfile); + } else { + context.startActivity(intent); + } + return true; + } +} diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java index 1b270d63b4d..71c51136d04 100644 --- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java @@ -48,6 +48,7 @@ import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; +import com.android.internal.gmscompat.GmsCompatApp; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; @@ -208,6 +209,15 @@ public void onDestroy() { } private class UninstallAndDisableButtonListener implements View.OnClickListener { + private boolean mChangeEnabledStateOfUserApp; + + UninstallAndDisableButtonListener() { + this(false); + } + + UninstallAndDisableButtonListener(boolean changeEnabledStateOfUserApp) { + mChangeEnabledStateOfUserApp = changeEnabledStateOfUserApp; + } @Override public void onClick(View v) { @@ -239,8 +249,13 @@ public void onClick(View v) { mUserId); if (admin != null && !uninstallBlockedBySystem) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity, admin); - } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || mChangeEnabledStateOfUserApp) { if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { + if (mChangeEnabledStateOfUserApp) { + handleDialogClick(ButtonActionDialogFragment.DialogType.DISABLE); + return; + } + // If the system app has an update and this is the only user on the device, // then offer to downgrade the app, otherwise only offer to disable the // app for this user. @@ -488,6 +503,25 @@ void updateUninstallButton() { } mButtonsPref.setButton2Enabled(enabled); + + if (enabled && !isBundled) { + // "enabled" means "show uninstall button" in this context + int text; + int icon; + if (mAppEntry.info.enabled) { + text = R.string.disable_text; + icon = R.drawable.ic_settings_disable; + } else { + text = R.string.enable_text; + icon = R.drawable.ic_settings_enable; + } + mButtonsPref + .setButton4Text(text) + .setButton4Icon(icon) + .setButton4Visible(true) + .setButton4OnClickListener(new UninstallAndDisableButtonListener(true)) + ; + } } /** @@ -514,7 +548,9 @@ private void refreshAndFinishIfPossible(boolean removeTaskWhenFinishing) { @VisibleForTesting void updateForceStopButton() { - if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { + if (!mPackageInfo.applicationInfo.enabled) { + mButtonsPref.setButton3Visible(false); + } else if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { // User can't force stop device admin. Log.w(TAG, "User can't force stop device admin"); updateForceStopButtonInner(false /* enabled */); @@ -538,6 +574,7 @@ void updateForceStopButton() { @VisibleForTesting void updateForceStopButtonInner(boolean enabled) { + mButtonsPref.setButton3Visible(true); if (mAppsControlDisallowedBySystem) { mButtonsPref.setButton3Enabled(false); } else { @@ -552,6 +589,7 @@ void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { Uri packageUri = Uri.parse("package:" + packageName); Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); + uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_SHOW_MORE_OPTIONS_BUTTON, false); mMetricsFeatureProvider.action( mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 54455d4f110..e672d39486f 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -178,6 +178,7 @@ public void onAttach(Context context) { use(AppAllServicesPreferenceController.class).setPackageName(packageName); use(AppStoragePreferenceController.class).setParentFragment(this); use(AppVersionPreferenceController.class).setParentFragment(this); + use(AppStorageScopesPreferenceController.class).setParentFragment(this); use(InstantAppDomainsPreferenceController.class).setParentFragment(this); final HibernationSwitchPreferenceController appHibernationSettings = @@ -215,7 +216,12 @@ public void onAttach(Context context) { alarmsAndReminders.setPackageName(packageName); alarmsAndReminders.setParentFragment(this); + final AppRelaxHardeningPreferenceController relaxHardening = + use(AppRelaxHardeningPreferenceController.class); + relaxHardening.setParentFragment(this); + use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList( + relaxHardening, writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles, alarmsAndReminders)); diff --git a/src/com/android/settings/applications/appinfo/AppRelaxHardeningPreferenceController.java b/src/com/android/settings/applications/appinfo/AppRelaxHardeningPreferenceController.java new file mode 100644 index 00000000000..20c12806041 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppRelaxHardeningPreferenceController.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2022 GrapheneOS + * + * 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.applications.appinfo; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.GosPackageState; +import android.os.Bundle; +import android.provider.Settings; + +import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import com.android.settings.R; +import com.android.settings.applications.AppInfoBase; +import com.android.settings.applications.AppInfoWithHeader; +import com.android.settingslib.applications.AppUtils; + +import dalvik.system.VMRuntime; + +public class AppRelaxHardeningPreferenceController extends AppInfoPreferenceControllerBase + implements Preference.OnPreferenceChangeListener { + + private final boolean devMode; + + public AppRelaxHardeningPreferenceController(Context ctx, String key) { + super(ctx, key); + + devMode = Settings.Global.getInt(ctx.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; + } + + @Override + public int getAvailabilityStatus() { + if (!AppUtils.isAppInstalled(mAppEntry)) { + // not installed for the current user, App info page is still shown in Owner in this case + return CONDITIONALLY_UNAVAILABLE; + } + + ApplicationInfo ai = mParent.getPackageInfo().applicationInfo; + return GosPackageState.eligibleForRelaxHardeningFlag(ai) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + private boolean addedDevPreference; + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + + final int flags = GosPackageState.FLAG_DISABLE_HARDENED_MALLOC | GosPackageState.FLAG_ENABLE_COMPAT_VA_39_BIT; + + GosPackageState ps = GosPackageState.get(getPackageName()); + boolean checked = ps != null && ps.hasFlags(flags); + + ((SwitchPreference) preference).setChecked(checked); + + if (!devMode || addedDevPreference || getAvailabilityStatus() != AVAILABLE) { + return; + } + + Preference p = new Preference(preference.getContext()); + p.setTitle(R.string.app_hardening_config); + + p.setOnPreferenceClickListener(pref -> { + AppInfoBase.startAppInfoFragment(DevModeFragment.class, + mContext.getString(R.string.app_hardening_config), + getPackageName(), mParent.getPackageInfo().applicationInfo.uid, + mParent, -1, mParent.getMetricsCategory()); + return true; + }); + + p.setOrder(preference.getOrder() - 1); + preference.getParent().addPreference(p); + + addedDevPreference = true; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean checked = (boolean) newValue; + + GosPackageState.edit(getPackageName()) + .setFlagsState(GosPackageState.FLAG_DISABLE_HARDENED_MALLOC + | GosPackageState.FLAG_ENABLE_COMPAT_VA_39_BIT, checked) + .killUidAfterApply() + .apply(); + + return true; + } + + private String getPackageName() { + return mParent.getPackageInfo().packageName; + } + + public static class DevModeFragment extends AppInfoWithHeader { + private static final String KEY_FLAG = "flag"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Context ctx = requireContext(); + PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(ctx); + + String[] flags = { "DISABLE_HARDENED_MALLOC", "ENABLE_COMPAT_VA_39_BIT" }; + + for (String flag : flags) { + SwitchPreference p = new SwitchPreference(ctx); + p.setTitle(flag); + + int psFlag; + try { + psFlag = GosPackageState.class.getDeclaredField("FLAG_" + flag).getInt(null); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + p.getExtras().putInt(KEY_FLAG, psFlag); + + p.setOnPreferenceChangeListener((preference, newValue) -> { + boolean state = (boolean) newValue; + + GosPackageState.edit(mPackageName) + .setFlagsState(psFlag, state) + .killUidAfterApply() + .apply(); + + return true; + }); + + screen.addPreference(p); + } + + setPreferenceScreen(screen); + } + + @Override + protected boolean refreshUi() { + GosPackageState ps = GosPackageState.get(mPackageName); + + PreferenceScreen s = getPreferenceScreen(); + for (int i = 0, m = s.getPreferenceCount(); i < m; ++i) { + Preference p = s.getPreference(i); + if (!(p instanceof SwitchPreference)) { + continue; + } + SwitchPreference sp = (SwitchPreference) p; + int psFlag = sp.getExtras().getInt(KEY_FLAG); + sp.setChecked(ps != null && ps.hasFlag(psFlag)); + } + + return true; + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + return null; + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + } +} diff --git a/src/com/android/settings/applications/appinfo/AppStorageScopesPreferenceController.java b/src/com/android/settings/applications/appinfo/AppStorageScopesPreferenceController.java new file mode 100644 index 00000000000..8014915e0de --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppStorageScopesPreferenceController.java @@ -0,0 +1,38 @@ +package com.android.settings.applications.appinfo; + +import android.app.StorageScope; +import android.content.Context; +import android.content.pm.GosPackageState; +import android.text.TextUtils; + +import androidx.preference.Preference; + +public class AppStorageScopesPreferenceController extends AppInfoPreferenceControllerBase { + public AppStorageScopesPreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public int getAvailabilityStatus() { + GosPackageState ps = GosPackageState.get(getPackageName()); + if (ps != null && ps.hasFlag(GosPackageState.FLAG_STORAGE_SCOPES_ENABLED)) { + return AVAILABLE; + } + + return CONDITIONALLY_UNAVAILABLE; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return false; + } + + mContext.startActivity(StorageScope.createConfigActivityIntent(getPackageName())); + return true; + } + + private String getPackageName() { + return mParent.getPackageInfo().packageName; + } +} diff --git a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java index 23dd9602f77..734bd6ff476 100644 --- a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java @@ -22,6 +22,9 @@ import com.android.settings.R; +import java.text.DateFormat; +import java.util.Date; + public class AppVersionPreferenceController extends AppInfoPreferenceControllerBase { public AppVersionPreferenceController(Context context, String key) { @@ -36,7 +39,40 @@ public CharSequence getSummary() { if (packageInfo == null) { return null; } - return mContext.getString(R.string.version_text, - BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName)); + + Context ctx = mContext; + + DateFormat dateFormat = android.text.format.DateFormat.getMediumDateFormat(ctx); + DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(ctx); + + String times = null; + if (packageInfo.firstInstallTime != 0) { + String s = formatDate(packageInfo.firstInstallTime, dateFormat, timeFormat); + times = ctx.getString(R.string.app_info_install_time, s); + } + + if (packageInfo.lastUpdateTime != 0 && packageInfo.lastUpdateTime != packageInfo.firstInstallTime) { + String s = formatDate(packageInfo.lastUpdateTime, dateFormat, timeFormat); + String updateTime = ctx.getString(R.string.app_info_update_time, s); + if (times != null) { + times += '\n' + updateTime; + } else { + times = updateTime; + } + } + + return ctx.getString(R.string.version_text, + BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName)) + + "\n\n" + packageInfo.packageName + + "\nversionCode " + packageInfo.getLongVersionCode() + + "\n\ntargetSdk " + packageInfo.applicationInfo.targetSdkVersion + + "\nminSdk " + packageInfo.applicationInfo.minSdkVersion + + (times != null ? ("\n\n" + times) : "") + ; + } + + private static String formatDate(long unixTs, DateFormat dateFormat, DateFormat timeFormat) { + Date d = new Date(unixTs); + return dateFormat.format(d) + "; " + timeFormat.format(d); } } diff --git a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java index b7232744365..f1ab2820520 100644 --- a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java +++ b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java @@ -21,6 +21,7 @@ import android.app.AppOpsManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.pm.GosPackageState; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -133,6 +134,9 @@ protected boolean refreshUi() { return true; } mSwitchPref.setChecked(mInstallAppsState.canInstallApps()); + + setupObbToggle(); + return true; } @@ -145,4 +149,55 @@ protected AlertDialog createDialog(int id, int errorCode) { public int getMetricsCategory() { return SettingsEnums.MANAGE_EXTERNAL_SOURCES; } + + private RestrictedSwitchPreference mObbPref; + + private void setupObbToggle() { + RestrictedSwitchPreference p = mObbPref; + if (p == null) { + if (!GosPackageState.attachableToPackage(mPackageName)) { + return; + } + p = createObbToggle(); + mSwitchPref.getParent().addPreference(p); + mObbPref = p; + } + + boolean canInstallApps = mInstallAppsState.canInstallApps(); + + if (!canInstallApps) { + p.setChecked(false); + p.callChangeListener(Boolean.FALSE); + } else { + GosPackageState ps = GosPackageState.get(mPackageName); + p.setChecked(ps != null && ps.hasFlag(GosPackageState.FLAG_ALLOW_ACCESS_TO_OBB_DIRECTORY)); + } + + p.setEnabled(canInstallApps); + } + + private RestrictedSwitchPreference createObbToggle() { + RestrictedSwitchPreference p = new RestrictedSwitchPreference(mSwitchPref.getContext()); + p.setTitle(R.string.allow_access_to_obb_directory_title); + p.setSummary(R.string.allow_access_to_obb_directory_summary); + + p.setOnPreferenceChangeListener((preference, checkedB) -> { + var ps = GosPackageState.get(mPackageName); + boolean curValue = ps != null && ps.hasFlag(GosPackageState.FLAG_ALLOW_ACCESS_TO_OBB_DIRECTORY); + + if (curValue == (boolean) checkedB) { + return true; + } + + GosPackageState.edit(mPackageName) + .setFlagsState(GosPackageState.FLAG_ALLOW_ACCESS_TO_OBB_DIRECTORY, (boolean) checkedB) + // storage mount modes can't be updated dynamically + .killUidAfterApply() + .apply(); + + return true; + }); + + return p; + } } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index d335324a339..ea3c568ee2f 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -767,9 +767,11 @@ void updateOptionsMenu() { } mOptionsMenu.findItem(R.id.advanced).setVisible(false); - mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE + mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible( + (mListType == LIST_TYPE_STORAGE || mListType == LIST_TYPE_MAIN) && mSortOrder != R.id.sort_order_alpha); - mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE + mOptionsMenu.findItem(R.id.sort_order_size).setVisible( + (mListType == LIST_TYPE_STORAGE || mListType == LIST_TYPE_MAIN) && mSortOrder != R.id.sort_order_size); mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java index 4cd2f790620..9cc656cda23 100644 --- a/src/com/android/settings/biometrics/BiometricUtils.java +++ b/src/com/android/settings/biometrics/BiometricUtils.java @@ -39,6 +39,7 @@ import com.android.settings.biometrics.face.FaceEnrollIntroduction; import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction; +import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; @@ -152,9 +153,13 @@ public static Intent getChooseLockIntent(@NonNull Context context, */ public static Intent getFingerprintFindSensorIntent(@NonNull Context context, @NonNull Intent activityIntent) { - Intent intent = new Intent(context, FingerprintEnrollFindSensor.class); - SetupWizardUtils.copySetupExtras(activityIntent, intent); - return intent; + if (WizardManagerHelper.isAnySetupWizard(activityIntent)) { + Intent intent = new Intent(context, SetupFingerprintEnrollFindSensor.class); + SetupWizardUtils.copySetupExtras(activityIntent, intent); + return intent; + } else { + return new Intent(context, FingerprintEnrollFindSensor.class); + } } /** diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java index ed74d2a550d..431b788ad73 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java @@ -202,32 +202,32 @@ protected boolean generateChallengeOnCreate() { @StringRes protected int getInfoMessageGlasses() { - return R.string.security_settings_face_enroll_introduction_info_glasses; + return R.string.security_settings_face_enroll_introduction_info_glasses_en; } @StringRes protected int getInfoMessageLooking() { - return R.string.security_settings_face_enroll_introduction_info_looking; + return R.string.security_settings_face_enroll_introduction_info_looking_en; } @StringRes protected int getInfoMessageRequireEyes() { - return R.string.security_settings_face_enroll_introduction_info_gaze; + return R.string.security_settings_face_enroll_introduction_info_gaze_en; } @StringRes protected int getHowMessage() { - return R.string.security_settings_face_enroll_introduction_how_message; + return R.string.security_settings_face_enroll_introduction_how_message_en; } @StringRes protected int getInControlTitle() { - return R.string.security_settings_face_enroll_introduction_control_title; + return R.string.security_settings_face_enroll_introduction_control_title_en; } @StringRes protected int getInControlMessage() { - return R.string.security_settings_face_enroll_introduction_control_message; + return R.string.security_settings_face_enroll_introduction_control_message_en; } @StringRes diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index bbb4f45b702..3a3b9e13839 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -41,6 +41,7 @@ import android.os.UserManager; import android.text.InputFilter; import android.text.Spanned; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -153,7 +154,7 @@ private static class FooterColumn { private static final String KEY_FINGERPRINT_ITEM_PREFIX = "key_fingerprint_item"; private static final String KEY_FINGERPRINT_ADD = "key_fingerprint_add"; private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE = - "fingerprint_enable_keyguard_toggle"; + "security_settings_fingerprint_keyguard"; private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm"; private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled"; private static final String KEY_IS_ENROLLING = "is_enrolled"; @@ -247,9 +248,7 @@ public void onRemovalError(Fingerprint fp, int errMsgId, } private void updateDialog() { - if (isSfps()) { - setRequireScreenOnToAuthVisibility(); - } + updateFingerprintUnlockCategory(); RenameDialog renameDialog = (RenameDialog) getFragmentManager(). findFragmentByTag(RenameDialog.class.getName()); if (renameDialog != null) { @@ -512,19 +511,36 @@ private PreferenceScreen createPreferenceHierarchy() { }); mFingerprintUnlockCategory.setVisible(false); if (isSfps()) { - setRequireScreenOnToAuthVisibility(); + updateFingerprintUnlockCategory(); } setPreferenceScreen(root); + + // Don't show keyguard preferences for work profile settings. + if (UserManager.get(getContext()).isManagedProfile(mUserId)) { + removePreference(KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE); + } else { + SwitchPreference lockScreenFingerprintPreference = + (SwitchPreference) findPreference(KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE); + + lockScreenFingerprintPreference.setChecked(Settings.Secure.getInt( + getContext().getContentResolver(), + Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED, 1) == 1); + lockScreenFingerprintPreference.setOnPreferenceChangeListener(this); + + updateFingerprintUnlockCategory(); + } + return root; } - private void setRequireScreenOnToAuthVisibility() { + private void updateFingerprintUnlockCategory() { int fingerprintsEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size(); final boolean removalInProgress = mRemovalSidecar.inProgress(); // Removing last remaining fingerprint if (fingerprintsEnrolled == 0 && removalInProgress) { mFingerprintUnlockCategory.setVisible(false); } else { + mRequireScreenOnToAuthPreference.setVisible(isSfps()); mFingerprintUnlockCategory.setVisible(true); } } @@ -735,7 +751,10 @@ public boolean onPreferenceChange(Preference preference, Object value) { boolean result = true; final String key = preference.getKey(); if (KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE.equals(key)) { - // TODO + boolean enableFingerprintUnlock = (boolean) value; + Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED, + (enableFingerprintUnlock) ? 1 : 0); } else { Log.v(TAG, "Unknown key:" + key); } diff --git a/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java new file mode 100644 index 00000000000..244147948aa --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.provider.Settings; +import android.util.Log; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; + +public class BluetoothTimeoutPreferenceController extends BasePreferenceController implements + PreferenceControllerMixin, Preference.OnPreferenceChangeListener { + private static final String TAG = "BluetoothTimeoutPrefCtrl"; + + public static final int FALLBACK_BLUETOOTH_TIMEOUT_VALUE = 0; + + private final String mBluetoothTimeoutKey; + + protected BluetoothAdapter mBluetoothAdapter; + + public BluetoothTimeoutPreferenceController(Context context, String key) { + super(context, key); + mBluetoothTimeoutKey = key; + + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (mBluetoothAdapter == null) { + Log.e(TAG, "Bluetooth is not supported on this device"); + return; + } + } + + @Override + public int getAvailabilityStatus() { + return mBluetoothAdapter != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return mBluetoothTimeoutKey; + } + + @Override + public void updateState(Preference preference) { + final ListPreference timeoutListPreference = (ListPreference) preference; + final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_OFF_TIMEOUT, FALLBACK_BLUETOOTH_TIMEOUT_VALUE); + timeoutListPreference.setValue(String.valueOf(currentTimeout)); + updateTimeoutPreferenceDescription(timeoutListPreference, + Long.parseLong(timeoutListPreference.getValue())); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + try { + long value = Long.parseLong((String) newValue); + Settings.Global.putLong(mContext.getContentResolver(), Settings.Global.BLUETOOTH_OFF_TIMEOUT, value); + updateTimeoutPreferenceDescription((ListPreference) preference, value); + } catch (NumberFormatException e) { + Log.e(TAG, "could not persist bluetooth timeout setting", e); + } + return true; + } + + public static CharSequence getTimeoutDescription( + long currentTimeout, CharSequence[] entries, CharSequence[] values) { + if (currentTimeout < 0 || entries == null || values == null + || values.length != entries.length) { + return null; + } + + for (int i = 0; i < values.length; i++) { + long timeout = Long.parseLong(values[i].toString()); + if (currentTimeout == timeout) { + return entries[i]; + } + } + return null; + } + + private void updateTimeoutPreferenceDescription(ListPreference preference, + long currentTimeout) { + final CharSequence[] entries = preference.getEntries(); + final CharSequence[] values = preference.getEntryValues(); + final CharSequence timeoutDescription = getTimeoutDescription( + currentTimeout, entries, values); + String summary = ""; + if (timeoutDescription != null) { + if (currentTimeout != 0) + summary = mContext.getString(R.string.bluetooth_timeout_summary, timeoutDescription); + else + summary = mContext.getString(R.string.bluetooth_timeout_summary2); + } + preference.setSummary(summary); + } +} diff --git a/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java b/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java index ee0021ec951..f16dd378db5 100644 --- a/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java +++ b/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java @@ -16,21 +16,47 @@ package com.android.settings.connecteddevice; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.nfc.NfcAdapter; import android.os.UserManager; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.core.lifecycle.events.OnStop; /** * Controller that used to show NFC and payment features */ -public class NfcAndPaymentFragmentController extends BasePreferenceController { +public class NfcAndPaymentFragmentController extends BasePreferenceController + implements LifecycleObserver, OnResume, OnStop { private final NfcAdapter mNfcAdapter; private final PackageManager mPackageManager; private final UserManager mUserManager; + private final IntentFilter mIntentFilter; + private Preference mPreference; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mPreference == null) { + return; + } + + final String action = intent.getAction(); + if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(action)) { + refreshSummary(mPreference); + } + } + }; public NfcAndPaymentFragmentController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -38,6 +64,15 @@ public NfcAndPaymentFragmentController(Context context, String preferenceKey) { mPackageManager = context.getPackageManager(); mUserManager = context.getSystemService(UserManager.class); mNfcAdapter = NfcAdapter.getDefaultAdapter(context); + + mIntentFilter = isNfcAvailable() + ? new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED) : null; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); } @Override @@ -61,4 +96,26 @@ public CharSequence getSummary() { } return null; } + + @Override + public void onStop() { + if (!isNfcAvailable()) { + return; + } + + mContext.unregisterReceiver(mReceiver); + } + + @Override + public void onResume() { + if (!isNfcAvailable()) { + return; + } + + mContext.registerReceiver(mReceiver, mIntentFilter); + } + + private boolean isNfcAvailable() { + return mNfcAdapter != null; + } } diff --git a/src/com/android/settings/core/PreferenceControllerListHelper.java b/src/com/android/settings/core/PreferenceControllerListHelper.java index 6d450fe137f..18e67982583 100644 --- a/src/com/android/settings/core/PreferenceControllerListHelper.java +++ b/src/com/android/settings/core/PreferenceControllerListHelper.java @@ -28,6 +28,7 @@ import android.util.Log; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; +import com.android.settings.ext.BoolSettingPrefController; import com.android.settingslib.core.AbstractPreferenceController; import org.xmlpull.v1.XmlPullParserException; @@ -56,6 +57,7 @@ public static List getPreferenceControllersFromXml(Con try { preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER + | MetadataFlag.FLAG_NEED_BOOL_SETTING_FIELD | MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_FOR_WORK); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed to parse preference xml for getting controllers", e); @@ -65,6 +67,7 @@ public static List getPreferenceControllersFromXml(Con for (Bundle metadata : preferenceMetadata) { final String controllerName = metadata.getString(METADATA_CONTROLLER); if (TextUtils.isEmpty(controllerName)) { + BoolSettingPrefController.maybeAdd(context, metadata, controllers); continue; } BasePreferenceController controller; diff --git a/src/com/android/settings/core/PreferenceXmlParserUtils.java b/src/com/android/settings/core/PreferenceXmlParserUtils.java index a1a8d6731a7..d9a1c397b64 100644 --- a/src/com/android/settings/core/PreferenceXmlParserUtils.java +++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java @@ -68,6 +68,7 @@ public class PreferenceXmlParserUtils { MetadataFlag.FLAG_NEED_KEY, MetadataFlag.FLAG_NEED_PREF_TYPE, MetadataFlag.FLAG_NEED_PREF_CONTROLLER, + MetadataFlag.FLAG_NEED_BOOL_SETTING_FIELD, MetadataFlag.FLAG_NEED_PREF_TITLE, MetadataFlag.FLAG_NEED_PREF_SUMMARY, MetadataFlag.FLAG_NEED_PREF_ICON, @@ -91,11 +92,14 @@ public class PreferenceXmlParserUtils { int FLAG_UNAVAILABLE_SLICE_SUBTITLE = 1 << 11; int FLAG_FOR_WORK = 1 << 12; int FLAG_NEED_HIGHLIGHTABLE_MENU_KEY = 1 << 13; + + int FLAG_NEED_BOOL_SETTING_FIELD = 1 << 30; } public static final String METADATA_PREF_TYPE = "type"; public static final String METADATA_KEY = "key"; public static final String METADATA_CONTROLLER = "controller"; + public static final String METADATA_BOOL_SETTING_FIELD = "bool_setting_field"; public static final String METADATA_TITLE = "title"; public static final String METADATA_SUMMARY = "summary"; public static final String METADATA_ICON = "icon"; @@ -225,6 +229,10 @@ public static List extractMetadata(Context context, @XmlRes int xmlResId preferenceMetadata.putString(METADATA_CONTROLLER, getController(preferenceAttributes)); } + if (hasFlag(flags, MetadataFlag.FLAG_NEED_BOOL_SETTING_FIELD)) { + preferenceMetadata.putString(METADATA_BOOL_SETTING_FIELD, + getBoolSettingField(preferenceAttributes)); + } if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) { preferenceMetadata.putString(METADATA_TITLE, getTitle(preferenceAttributes)); } @@ -321,6 +329,10 @@ private static String getController(TypedArray styledAttributes) { return styledAttributes.getString(R.styleable.Preference_controller); } + private static String getBoolSettingField(TypedArray styledAttributes) { + return styledAttributes.getString(R.styleable.Preference_boolSettingField); + } + private static String getHighlightableMenuKey(TypedArray styledAttributes) { return styledAttributes.getString(R.styleable.Preference_highlightableMenuKey); } diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BootloaderVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/BootloaderVersionPreferenceController.java new file mode 100644 index 00000000000..32b1cb64de0 --- /dev/null +++ b/src/com/android/settings/deviceinfo/firmwareversion/BootloaderVersionPreferenceController.java @@ -0,0 +1,28 @@ +package com.android.settings.deviceinfo.firmwareversion; + +import android.content.Context; +import android.os.SystemProperties; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.core.BasePreferenceController; + +public class BootloaderVersionPreferenceController extends BasePreferenceController { + + static final String BOOTLOADER_PROPERTY = "ro.bootloader"; + + public BootloaderVersionPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public CharSequence getSummary() { + return SystemProperties.get(BOOTLOADER_PROPERTY, + mContext.getString(R.string.device_info_default)); + } +} diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareSkuPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareSkuPreferenceController.java new file mode 100644 index 00000000000..4be09710e8c --- /dev/null +++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareSkuPreferenceController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.deviceinfo.hardwareinfo; + +import android.content.Context; +import android.os.SystemProperties; +import android.text.TextUtils; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.slices.Sliceable; + +public class HardwareSkuPreferenceController extends BasePreferenceController { + + public HardwareSkuPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean(R.bool.config_show_device_model) && + !TextUtils.isEmpty(getSummary()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public boolean useDynamicSliceSummary() { + return true; + } + + @Override + public CharSequence getSummary() { + return SystemProperties.get("ro.boot.hardware.sku"); + } +} diff --git a/src/com/android/settings/display/BatterySharePreferenceController.java b/src/com/android/settings/display/BatterySharePreferenceController.java new file mode 100644 index 00000000000..214ceccedaa --- /dev/null +++ b/src/com/android/settings/display/BatterySharePreferenceController.java @@ -0,0 +1,98 @@ +package com.android.settings.display; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import com.android.internal.R; +import com.android.internal.batteryShare.ReverseWirelessCharger; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +public class BatterySharePreferenceController extends BasePreferenceController implements + PreferenceControllerMixin, Preference.OnPreferenceChangeListener, LifecycleObserver, + OnStart, OnStop { + + private static final String KEY_BATTERY_SHARE = "battery_share"; + private final ReverseWirelessCharger wirelessCharger; + private final Context mContext; + private Preference mPreference; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ((Activity) mContext).runOnUiThread(() -> update()); + } + }; + + + public BatterySharePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mContext = context; + wirelessCharger = ReverseWirelessCharger.getInstance(); + } + + public boolean isPlugged(Context context) { + Intent intent = context.registerReceiver(null, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + update(); + } + + @Override + public String getPreferenceKey() { + return KEY_BATTERY_SHARE; + } + + private void update() { + if (mPreference == null) return; + boolean enabled = !isPlugged(mContext) && wirelessCharger.isRtxSupported(); + mPreference.setEnabled(enabled); + ((SwitchPreference) mPreference).setChecked(wirelessCharger.isRtxModeOn()); + } + + @Override + public int getAvailabilityStatus() { + return wirelessCharger.isRtxSupported() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public void updateState(Preference preference) { + update(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + wirelessCharger.setRtxMode((Boolean) newValue); + return true; + } + + @Override + public void onStart() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, filter); + } + + @Override + public void onStop() { + mContext.unregisterReceiver(mBroadcastReceiver); + } +} diff --git a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java index 27ba340c6ba..382c812c3cf 100644 --- a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java +++ b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java @@ -89,6 +89,9 @@ public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreference = screen.findPreference(getPreferenceKey()); + mPreference.setSummary( + screen.getContext().getString(R.string.peak_refresh_rate_summary_dynamic, (int) getDefaultPeakRefreshRate()) + ); } @Override diff --git a/src/com/android/settings/display/ScreenResolutionFragment.java b/src/com/android/settings/display/ScreenResolutionFragment.java index 7c4b3aeef03..3e713aacbdb 100644 --- a/src/com/android/settings/display/ScreenResolutionFragment.java +++ b/src/com/android/settings/display/ScreenResolutionFragment.java @@ -93,7 +93,6 @@ protected int getPreferenceScreenResId() { @Override protected void addStaticPreferences(PreferenceScreen screen) { - updateIllustrationImage(mImagePreference); screen.addPreference(mImagePreference); final FooterPreference footerPreference = new FooterPreference(screen.getContext()); @@ -207,7 +206,6 @@ protected boolean setDefaultKey(final String key) { } setDisplayMode(width); - updateIllustrationImage(mImagePreference); return true; } diff --git a/src/com/android/settings/display/TouchSensitivityPreferenceController.java b/src/com/android/settings/display/TouchSensitivityPreferenceController.java new file mode 100755 index 00000000000..a6f90f593f0 --- /dev/null +++ b/src/com/android/settings/display/TouchSensitivityPreferenceController.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 The Proton AOSP 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.display; + +import android.content.Context; +import android.os.SystemProperties; +import android.provider.Settings; + +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.R; + +public class TouchSensitivityPreferenceController extends TogglePreferenceController { + + // Settings can only set the debug.* property, so we need to persist it + // in system settings. Match the stock setting name for backup compatibility. + private static final String SETTINGS_KEY = "touch_sensitivity_enabled"; + private static final String PROP_NAME = "debug.touch_sensitivity_mode"; + + public TouchSensitivityPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean(com.android.internal.R.bool.config_supportGloveMode) + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public boolean setChecked(boolean value) { + Settings.Secure.putInt(mContext.getContentResolver(), SETTINGS_KEY, value ? 1 : 0); + SystemProperties.set(PROP_NAME, value ? "1" : "0"); + return true; + } + + @Override + public boolean isChecked() { + // debug prop isn't persistent + return Settings.Secure.getInt(mContext.getContentResolver(), SETTINGS_KEY, 0) == 1; + } + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_display; + } +} diff --git a/src/com/android/settings/ext/AbstractListPreferenceController.java b/src/com/android/settings/ext/AbstractListPreferenceController.java new file mode 100644 index 00000000000..f4179c49cbb --- /dev/null +++ b/src/com/android/settings/ext/AbstractListPreferenceController.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 GrapheneOS + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.settings.ext; + +import android.content.Context; +import android.text.format.DateUtils; +import android.util.SparseIntArray; + +import androidx.annotation.StringRes; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.BasePreferenceController; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractListPreferenceController extends BasePreferenceController + implements Preference.OnPreferenceChangeListener { + + private ListPreference preference; + private Entries entries; + // current preference value is prepended to the baseSummary + private CharSequence baseSummary; + + protected AbstractListPreferenceController(Context ctx, String key) { + super(ctx, key); + } + + // call entries.add(entryName, entryValue) to add entries. + // entryValues can be mapped from other values or sets of values, as long as getCurrentValue() + // and setValue() methods are consistent + protected abstract void getEntries(Entries entries); + + protected abstract int getCurrentValue(); + protected abstract boolean setValue(int val); + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + ListPreference p = screen.findPreference(mPreferenceKey); + if (p == null) { + return; + } + + this.preference = p; + + if (p.getEntries() == null) { + if (entries == null) { + entries = new Entries(mContext); + getEntries(entries); + } + + baseSummary = p.getSummary(); + + p.setSingleLineTitle(false); + p.setEntries(entries.getTitles()); + p.setEntryValues(entries.getValues()); + p.setPersistent(false); + p.setOnPreferenceChangeListener(this); + } + + updatePreference(); + } + + void updatePreference() { + ListPreference p = preference; + if (p == null) { + return; + } + + int idx = entries.getIndexForValue(getCurrentValue()); + if (idx >= 0) { + p.setValueIndex(idx); + + var summary = new StringBuilder(); + summary.append("[ "); + summary.append(p.getEntries()[idx]); + summary.append(" ]"); + if (baseSummary != null) { + summary.append("\n\n"); + summary.append(baseSummary); + } + p.setSummary(summary.toString()); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + int val = Integer.parseInt((String) o); + return setValue(val); + } + + public static class Entries { + private final Context context; + private final ArrayList titles = new ArrayList<>(); + private final ArrayList values = new ArrayList<>(); + private final SparseIntArray valueToIndexMap = new SparseIntArray(); + + Entries(Context context) { + this.context = context; + } + + public void add(@StringRes int title, int value) { + add(context.getText(title), value); + } + + public void add(int duration, TimeUnit timeUnit) { + long durationMillis = timeUnit.toMillis(duration); + if (durationMillis > Integer.MAX_VALUE) { + throw new IllegalArgumentException(); + } + + add(DateUtils.formatDuration(durationMillis), (int) durationMillis); + } + + public void add(CharSequence title, int value) { + titles.add(title); + values.add(Integer.toString(value)); + valueToIndexMap.put(value, values.size() - 1); + } + + public CharSequence[] getTitles() { + return titles.toArray(CharSequence[]::new); + } + + public String[] getValues() { + return values.toArray(String[]::new); + } + + public int getIndexForValue(int val) { + return valueToIndexMap.get(val, -1); + } + } + + @Override + public boolean isSliceable() { + return false; + } + + @Override + public int getSliceHighlightMenuRes() { + return NO_RES; + } +} diff --git a/src/com/android/settings/ext/AbstractTogglePrefController.java b/src/com/android/settings/ext/AbstractTogglePrefController.java new file mode 100644 index 00000000000..ceee6984bfd --- /dev/null +++ b/src/com/android/settings/ext/AbstractTogglePrefController.java @@ -0,0 +1,38 @@ +package com.android.settings.ext; + +import android.content.Context; + +import androidx.annotation.Nullable; +import androidx.preference.Preference; + +import com.android.settings.core.TogglePreferenceController; + +public abstract class AbstractTogglePrefController extends TogglePreferenceController { + + protected AbstractTogglePrefController(Context ctx, String key) { + super(ctx, key); + } + + @Nullable protected Preference preference; + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + + if (preference != this.preference) { + preference.setSingleLineTitle(false); + preference.setPersistent(false); + this.preference = preference; + } + } + + @Override + public boolean isSliceable() { + return false; + } + + @Override + public int getSliceHighlightMenuRes() { + return NO_RES; + } +} diff --git a/src/com/android/settings/ext/BoolSettingPrefController.java b/src/com/android/settings/ext/BoolSettingPrefController.java new file mode 100644 index 00000000000..3a37f99f78e --- /dev/null +++ b/src/com/android/settings/ext/BoolSettingPrefController.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 GrapheneOS + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.settings.ext; + +import android.content.Context; +import android.ext.settings.BoolSetting; +import android.ext.settings.Setting; +import android.os.Bundle; +import android.os.Process; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; + +import com.android.settings.core.BasePreferenceController; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_BOOL_SETTING_FIELD; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; + +public class BoolSettingPrefController extends AbstractTogglePrefController + implements DefaultLifecycleObserver, Consumer { + private final BoolSetting setting; + + protected BoolSettingPrefController(Context ctx, String key, BoolSetting setting) { + super(ctx, key); + this.setting = setting; + } + + @Override + public int getAvailabilityStatus() { + if (setting.getScope() != Setting.Scope.PER_USER) { + if (!Process.myUserHandle().isSystem()) { + return DISABLED_FOR_USER; + } + } + return AVAILABLE; + } + + @Override + public final boolean isChecked() { + return setting.get(mContext); + } + + @Override + public final boolean setChecked(boolean isChecked) { + return setting.put(mContext, isChecked); + } + + private Object observer; + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + if (setting.canObserveState()) { + observer = setting.registerObserver(mContext, this, mContext.getMainThreadHandler()); + } + } + + // called by the setting observer + @Override + public void accept(BoolSetting boolSetting) { + if (preference != null) { + updateState(preference); + } + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + if (setting.canObserveState()) { + setting.unregisterObserver(mContext, observer); + } + } + + // called when PreferenceScreen XML is parsed + public static void maybeAdd(Context context, Bundle metadata, + List dest) { + String boolSettingField = metadata.getString(METADATA_BOOL_SETTING_FIELD); + if (boolSettingField == null) { + return; + } + String[] split = boolSettingField.split(" "); + + BoolSetting boolSetting; + try { + Class c = Class.forName(split[0]); + Field field = c.getField(split[1]); + boolSetting = (BoolSetting) Objects.requireNonNull(field.get(null)); + } catch (Exception e) { + throw new IllegalStateException("Invalid BoolSetting field " + boolSettingField); + } + + String key = Objects.requireNonNull(metadata.getString(METADATA_KEY)); + + dest.add(new BoolSettingPrefController(context, key, boolSetting)); + } +} diff --git a/src/com/android/settings/ext/IntSettingPrefController.java b/src/com/android/settings/ext/IntSettingPrefController.java new file mode 100644 index 00000000000..fb7574fa4e7 --- /dev/null +++ b/src/com/android/settings/ext/IntSettingPrefController.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 GrapheneOS + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.settings.ext; + +import android.content.Context; +import android.ext.settings.IntSetting; +import android.ext.settings.Setting; +import android.os.Process; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; + +import java.util.function.Consumer; + +public abstract class IntSettingPrefController extends AbstractListPreferenceController + implements DefaultLifecycleObserver, Consumer +{ + private final IntSetting setting; + + protected IntSettingPrefController(Context ctx, String key, IntSetting setting) { + super(ctx, key); + this.setting = setting; + } + + @Override + public int getAvailabilityStatus() { + if (setting.getScope() == Setting.Scope.GLOBAL) { + if (!Process.myUserHandle().isSystem()) { + return DISABLED_FOR_USER; + } + } + return AVAILABLE; + } + + @Override + protected final int getCurrentValue() { + return setting.get(mContext); + } + + @Override + protected final boolean setValue(int val) { + return setting.put(mContext, val); + } + + private Object observer; + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + if (setting.canObserveState()) { + observer = setting.registerObserver(mContext, this, mContext.getMainThreadHandler()); + } + } + + // called by the setting observer + @Override + public void accept(IntSetting intSetting) { + updatePreference(); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + if (setting.canObserveState()) { + setting.unregisterObserver(mContext, observer); + } + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java index ccb2fb786eb..748aa494d4d 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java @@ -606,9 +606,10 @@ public static NameAndIcon getNameAndIconFromPowerComponent( iconId = R.drawable.ic_settings_phone_idle; break; default: - Log.w(TAG, "unknown attribute:" + DebugUtils.constantToString( - BatteryConsumer.class, "POWER_COMPONENT_", powerComponentId)); - name = null; + String fieldName = DebugUtils.constantToString( + BatteryConsumer.class, "POWER_COMPONENT_", powerComponentId); + Log.w(TAG, "unknown attribute:" + fieldName); + name = context.getResources().getString(R.string.header_category_system) + " (" + fieldName + ")"; iconId = R.drawable.ic_power_system; break; } diff --git a/src/com/android/settings/location/GnssSuplPrefController.java b/src/com/android/settings/location/GnssSuplPrefController.java new file mode 100644 index 00000000000..65625b3bf20 --- /dev/null +++ b/src/com/android/settings/location/GnssSuplPrefController.java @@ -0,0 +1,22 @@ +package com.android.settings.location; + +import android.content.Context; +import android.ext.settings.ExtSettings; +import android.ext.settings.GnssConstants; + +import com.android.settings.R; +import com.android.settings.ext.IntSettingPrefController; + +public class GnssSuplPrefController extends IntSettingPrefController { + + public GnssSuplPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.GNSS_SUPL); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.supl_enabled_grapheneos_proxy, GnssConstants.SUPL_SERVER_GRAPHENEOS_PROXY); + entries.add(R.string.supl_enabled_standard_server, GnssConstants.SUPL_SERVER_STANDARD); + entries.add(R.string.supl_disabled, GnssConstants.SUPL_DISABLED); + } +} diff --git a/src/com/android/settings/location/LocationIndicatorsPreferenceController.java b/src/com/android/settings/location/LocationIndicatorsPreferenceController.java index 75ffb3a9286..1b1af8f8f73 100644 --- a/src/com/android/settings/location/LocationIndicatorsPreferenceController.java +++ b/src/com/android/settings/location/LocationIndicatorsPreferenceController.java @@ -34,7 +34,7 @@ public LocationIndicatorsPreferenceController(Context context, String preference @Override public boolean isChecked() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - Utils.PROPERTY_LOCATION_INDICATORS_ENABLED, false); + Utils.PROPERTY_LOCATION_INDICATORS_ENABLED, true); } @Override diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java index 61682d00a8c..6b28d921efa 100644 --- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java +++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java @@ -48,7 +48,7 @@ public class RecentLocationAccessPreferenceController extends LocationBasePrefer RecentAppOpsAccess mRecentLocationApps; private PreferenceCategory mCategoryRecentLocationRequests; private int mType = ProfileSelectFragment.ProfileType.ALL; - private boolean mShowSystem = false; + private boolean mShowSystem = true; private boolean mSystemSettingChanged = false; private static class PackageEntryClickedListener implements @@ -85,9 +85,9 @@ public RecentLocationAccessPreferenceController(Context context, String key, super(context, key); mRecentLocationApps = recentLocationApps; mShowSystem = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false) + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, true) ? Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0) == 1 + Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1) == 1 : false; } diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java b/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java index ecbcb30eace..1566712e405 100644 --- a/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java +++ b/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java @@ -40,7 +40,7 @@ public class RecentLocationAccessSeeAllFragment extends DashboardFragment { private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 1; private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 2; - private boolean mShowSystem = false; + private boolean mShowSystem = true; private MenuItem mShowSystemMenu; private MenuItem mHideSystemMenu; private RecentLocationAccessSeeAllPreferenceController mController; @@ -61,9 +61,9 @@ public void onAttach(Context context) { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mShowSystem = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false) + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, true) ? Settings.Secure.getInt(getContentResolver(), - Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0) == 1 + Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1) == 1 : false; } diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java index d6586673f89..abda482a1e5 100644 --- a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java +++ b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java @@ -51,9 +51,9 @@ public class RecentLocationAccessSeeAllPreferenceController public RecentLocationAccessSeeAllPreferenceController(Context context, String key) { super(context, key); mShowSystem = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false) + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, true) ? Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0) == 1 + Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1) == 1 : false; mRecentLocationAccesses = RecentAppOpsAccess.createForLocation(context); diff --git a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java index 39211ee5202..bee904efdc4 100644 --- a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java +++ b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java @@ -87,9 +87,9 @@ public void displayPreference(PreferenceScreen screen) { final List recentLocationRequests = new ArrayList<>(); final UserManager userManager = UserManager.get(mContext); final boolean showSystem = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false) + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, true) ? Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0) == 1 + Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1) == 1 : false; for (RecentLocationApps.Request request : mRecentLocationApps.getAppListSorted( diff --git a/src/com/android/settings/network/ConnectivityCheckPreferenceController.java b/src/com/android/settings/network/ConnectivityCheckPreferenceController.java new file mode 100644 index 00000000000..adbe8978772 --- /dev/null +++ b/src/com/android/settings/network/ConnectivityCheckPreferenceController.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.LinkProperties; +import android.net.Network; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.internal.util.ArrayUtils; +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class ConnectivityCheckPreferenceController + extends BasePreferenceController + implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, + OnResume { + + private static final String GRAPHENEOS_CAPTIVE_PORTAL_HTTPS_URL = + "https://connectivitycheck.grapheneos.network/generate_204"; + private static final String GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL = + "http://connectivitycheck.grapheneos.network/generate_204"; + private static final String GRAPHENEOS_CAPTIVE_PORTAL_FALLBACK_URL = + "http://grapheneos.online/gen_204"; + private static final String GRAPHENEOS_CAPTIVE_PORTAL_OTHER_FALLBACK_URL = + "http://grapheneos.online/generate_204"; + + // imported defaults from AOSP NetworkStack + private static final String STANDARD_HTTPS_URL = + "https://www.google.com/generate_204"; + private static final String STANDARD_HTTP_URL = + "http://connectivitycheck.gstatic.com/generate_204"; + private static final String STANDARD_FALLBACK_URL = + "http://www.google.com/gen_204"; + private static final String STANDARD_OTHER_FALLBACK_URLS = + "http://play.googleapis.com/generate_204"; + + private static final int GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL_INTVAL = 0; + private static final int STANDARD_CAPTIVE_PORTAL_HTTP_URL_INTVAL = 1; + private static final int DISABLED_CAPTIVE_PORTAL_INTVAL = 2; + + private static final String KEY_CONNECTIVITY_CHECK_SETTINGS = + "connectivity_check_settings"; + + private ListPreference mConnectivityPreference; + + public ConnectivityCheckPreferenceController(Context context) { + super(context, KEY_CONNECTIVITY_CHECK_SETTINGS); + } + + @Override + public int getAvailabilityStatus() { + if (isDisabledByAdmin()) { + return BasePreferenceController.DISABLED_FOR_USER; + } + return BasePreferenceController.AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + ListPreference captiveList = new ListPreference(screen.getContext()); + captiveList.setKey(KEY_CONNECTIVITY_CHECK_SETTINGS); + captiveList.setOrder(30); + captiveList.setIcon(R.drawable.ic_settings_language); + captiveList.setTitle(R.string.connectivity_check_title); + captiveList.setSummary(R.string.connectivity_check_summary); + captiveList.setEntries(R.array.connectivity_check_entries); + captiveList.setEntryValues(R.array.connectivity_check_values); + + if (mConnectivityPreference == null) { + screen.addPreference(captiveList); + mConnectivityPreference = captiveList; + } + super.displayPreference(screen); + updatePreferenceState(); + } + + @Override + public String getPreferenceKey() { + return KEY_CONNECTIVITY_CHECK_SETTINGS; + } + + private void updatePreferenceState() { + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT) + == Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE) { + mConnectivityPreference.setValueIndex(DISABLED_CAPTIVE_PORTAL_INTVAL); + return; + } + + String pref = Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + if (STANDARD_HTTP_URL.equals(pref)) { + mConnectivityPreference.setValueIndex( + STANDARD_CAPTIVE_PORTAL_HTTP_URL_INTVAL); + } else { + mConnectivityPreference.setValueIndex( + GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL_INTVAL); + } + } + + @Override + public void onResume() { + updatePreferenceState(); + if (mConnectivityPreference != null) { + setCaptivePortalURLs( + mContext.getContentResolver(), + Integer.parseInt(mConnectivityPreference.getValue())); + } + } + + private void setCaptivePortalURLs(ContentResolver cr, int mode) { + switch (mode) { + case STANDARD_CAPTIVE_PORTAL_HTTP_URL_INTVAL: + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, + STANDARD_HTTP_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, + STANDARD_HTTPS_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, + STANDARD_FALLBACK_URL); + Settings.Global.putString( + cr, Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, + STANDARD_OTHER_FALLBACK_URLS); + Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, + Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); + break; + case GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL_INTVAL: + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, + GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, + GRAPHENEOS_CAPTIVE_PORTAL_HTTPS_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, + GRAPHENEOS_CAPTIVE_PORTAL_FALLBACK_URL); + Settings.Global.putString( + cr, Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, + GRAPHENEOS_CAPTIVE_PORTAL_OTHER_FALLBACK_URL); + Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, + Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); + break; + default: + // GrapheneOS URLs as placeholder + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, + GRAPHENEOS_CAPTIVE_PORTAL_HTTP_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, + GRAPHENEOS_CAPTIVE_PORTAL_HTTPS_URL); + Settings.Global.putString(cr, Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, + GRAPHENEOS_CAPTIVE_PORTAL_FALLBACK_URL); + Settings.Global.putString( + cr, Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, + GRAPHENEOS_CAPTIVE_PORTAL_OTHER_FALLBACK_URL); + Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, + Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + final String key = preference.getKey(); + if (KEY_CONNECTIVITY_CHECK_SETTINGS.equals(key)) { + setCaptivePortalURLs(mContext.getContentResolver(), + Integer.parseInt((String)value)); + return true; + } else { + return false; + } + } + + private EnforcedAdmin getEnforcedAdmin() { + return RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + mContext, UserManager.DISALLOW_CONFIG_PRIVATE_DNS, + UserHandle.myUserId()); + } + + private boolean isDisabledByAdmin() { return getEnforcedAdmin() != null; } +} diff --git a/src/com/android/settings/network/GoogleEuiccLpaController.java b/src/com/android/settings/network/GoogleEuiccLpaController.java new file mode 100644 index 00000000000..db6868fe0bc --- /dev/null +++ b/src/com/android/settings/network/GoogleEuiccLpaController.java @@ -0,0 +1,122 @@ +package com.android.settings.network; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.os.PatternMatcher; +import android.os.Process; +import android.os.UserHandle; +import android.permission.PermissionManager; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.Preference; +import androidx.preference.TwoStatePreference; + +import com.android.internal.util.GoogleEuicc; +import com.android.settings.ext.AbstractTogglePrefController; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + +public class GoogleEuiccLpaController extends AbstractTogglePrefController implements DefaultLifecycleObserver { + private final PackageManager packageManager; + + public GoogleEuiccLpaController(Context context, String key) { + super(context, key); + packageManager = context.getPackageManager(); + } + + @Override + public boolean isChecked() { + try { + return packageManager.getApplicationEnabledSetting(GoogleEuicc.LPA_PKG_NAME) + == COMPONENT_ENABLED_STATE_ENABLED; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return false; + } + } + + @Override + public int getAvailabilityStatus() { + if (!Process.myUserHandle().isSystem()) { + return DISABLED_FOR_USER; + } + + return GoogleEuicc.checkLpaDependencies() ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + preference.setEnabled(getAvailabilityStatus() == AVAILABLE); + } + + @Override + public boolean setChecked(boolean isChecked) { + int state = isChecked && GoogleEuicc.checkLpaDependencies() ? + COMPONENT_ENABLED_STATE_ENABLED : + COMPONENT_ENABLED_STATE_DISABLED; + + PermissionManager permissionManager = mContext.getSystemService(PermissionManager.class); + + try { + // Previously, Camera permission was auto-granted with the FLAG_PERMISSION_SYSTEM_FIXED, + // which made it unchangeable by the user. + // Removing FLAG_PERMISSION_USER_FIXED is needed to make sure that the app is always + // able to show a permission request dialog after being enabled + String pkg = GoogleEuicc.LPA_PKG_NAME; + if (state == COMPONENT_ENABLED_STATE_ENABLED) { + UserHandle user = mContext.getUser(); + String perm = Manifest.permission.CAMERA; + permissionManager.revokeRuntimePermission(pkg, perm, user, null); + int permFlagsToRemove = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED + | PackageManager.FLAG_PERMISSION_USER_FIXED; + permissionManager.updatePermissionFlags(pkg, perm, permFlagsToRemove, 0, user); + } + packageManager.setApplicationEnabledSetting(pkg, state, 0); + + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + private final BroadcastReceiver packageChangeListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (preference != null) { + updateState(preference); + } + } + }; + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + var f = new IntentFilter(); + f.addAction(Intent.ACTION_PACKAGE_ADDED); + f.addAction(Intent.ACTION_PACKAGE_CHANGED); + f.addAction(Intent.ACTION_PACKAGE_REMOVED); + f.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + + f.addDataScheme("package"); + for (String pkg : GoogleEuicc.getLpaDependencies()) { + f.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL); + } + + f.addDataSchemeSpecificPart(GoogleEuicc.LPA_PKG_NAME, PatternMatcher.PATTERN_LITERAL); + + mContext.registerReceiver(packageChangeListener, f); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + mContext.unregisterReceiver(packageChangeListener); + } +} diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java index 0cd24dc2a28..63da2a6e50e 100644 --- a/src/com/android/settings/network/NetworkDashboardFragment.java +++ b/src/com/android/settings/network/NetworkDashboardFragment.java @@ -121,6 +121,12 @@ private static List buildPreferenceControllers(Con } controllers.add(privateDnsPreferenceController); controllers.add(new NetworkProviderCallsSmsController(context, lifecycle)); + ConnectivityCheckPreferenceController connectivityCheck = + new ConnectivityCheckPreferenceController(context); + controllers.add(connectivityCheck); + PsdsServerPreferenceController psdsServer = + new PsdsServerPreferenceController(context); + controllers.add(psdsServer); return controllers; } diff --git a/src/com/android/settings/network/PsdsServerPreferenceController.java b/src/com/android/settings/network/PsdsServerPreferenceController.java new file mode 100644 index 00000000000..645ce41d1f5 --- /dev/null +++ b/src/com/android/settings/network/PsdsServerPreferenceController.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 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; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.LinkProperties; +import android.net.Network; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.Process; +import android.provider.Settings; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.internal.util.ArrayUtils; +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class PsdsServerPreferenceController + extends BasePreferenceController + implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, + OnResume { + + private static final int GRAPHENEOS_PSDS_SERVER_INTVAL = 0; + private static final int STANDARD_PSDS_SERVER_INTVAL = 1; + + private static final String KEY_PSDS_SERVER_SETTINGS = + "psds_server_settings"; + + private ListPreference mServerPreference; + + public PsdsServerPreferenceController(Context context) { + super(context, KEY_PSDS_SERVER_SETTINGS); + } + + @Override + public int getAvailabilityStatus() { + if (!Process.myUserHandle().isSystem()) { + return BasePreferenceController.DISABLED_FOR_USER; + } + String device = android.os.Build.HARDWARE; + if (!device.equals("oriole") && !device.equals("raven") && !device.equals("bluejay") && !device.equals("panther") && !device.equals("cheetah")) { + return BasePreferenceController.DISABLED_FOR_USER; + } + return BasePreferenceController.AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + ListPreference psdsServerList = new ListPreference(screen.getContext()); + psdsServerList.setKey(KEY_PSDS_SERVER_SETTINGS); + psdsServerList.setOrder(40); + psdsServerList.setTitle(R.string.psds_server_title); + psdsServerList.setSummary(R.string.psds_server_summary); + psdsServerList.setEntries(R.array.psds_server_entries); + psdsServerList.setEntryValues(R.array.psds_server_values); + + if (mServerPreference == null) { + screen.addPreference(psdsServerList); + mServerPreference = psdsServerList; + } + super.displayPreference(screen); + updatePreferenceState(); + } + + @Override + public String getPreferenceKey() { + return KEY_PSDS_SERVER_SETTINGS; + } + + private void updatePreferenceState() { + int pref = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PSDS_SERVER, GRAPHENEOS_PSDS_SERVER_INTVAL); + if (GRAPHENEOS_PSDS_SERVER_INTVAL == pref) { + mServerPreference.setValueIndex(GRAPHENEOS_PSDS_SERVER_INTVAL); + } else { + mServerPreference.setValueIndex(STANDARD_PSDS_SERVER_INTVAL); + } + } + + @Override + public void onResume() { + updatePreferenceState(); + if (mServerPreference != null) { + setPsdsUrl(mContext.getContentResolver(), Integer.parseInt(mServerPreference.getValue())); + } + } + + private void setPsdsUrl(ContentResolver cr, int mode) { + switch (mode) { + case STANDARD_PSDS_SERVER_INTVAL: + Settings.Global.putInt(cr, Settings.Global.PSDS_SERVER, STANDARD_PSDS_SERVER_INTVAL); + break; + case GRAPHENEOS_PSDS_SERVER_INTVAL: + Settings.Global.putInt(cr, Settings.Global.PSDS_SERVER, GRAPHENEOS_PSDS_SERVER_INTVAL); + break; + default: + // GrapheneOS URL as placeholder + Settings.Global.putInt(cr, Settings.Global.PSDS_SERVER, GRAPHENEOS_PSDS_SERVER_INTVAL); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + final String key = preference.getKey(); + if (KEY_PSDS_SERVER_SETTINGS.equals(key)) { + setPsdsUrl(mContext.getContentResolver(), Integer.parseInt((String)value)); + return true; + } else { + return false; + } + } + +} diff --git a/src/com/android/settings/network/RemoteProvisioningPrefController.java b/src/com/android/settings/network/RemoteProvisioningPrefController.java new file mode 100644 index 00000000000..2ce8702fac7 --- /dev/null +++ b/src/com/android/settings/network/RemoteProvisioningPrefController.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 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; + +import android.content.Context; +import android.ext.settings.ExtSettings; +import android.ext.settings.RemoteProvisioningConstants; + +import com.android.settings.R; +import com.android.settings.ext.IntSettingPrefController; + +public class RemoteProvisioningPrefController extends IntSettingPrefController { + + public RemoteProvisioningPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.REMOTE_PROVISIONING_SERVER); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.remote_provisioning_enabled_grapheneos_proxy, RemoteProvisioningConstants.GRAPHENEOS_PROXY); + entries.add(R.string.remote_provisioning_enabled_standard_server, RemoteProvisioningConstants.STANDARD_SERVER); + } +} diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java index 43b3cc0e4c4..1be4e6f4954 100644 --- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java @@ -90,7 +90,7 @@ public int getAvailabilityStatus(int subId) { CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) || carrierConfig.getBoolean( CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL)) { - visible = false; + visible = true; } else if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) { visible = false; } else if (!isCallStateIdle()) { @@ -237,6 +237,7 @@ public void updateConfig() { } void setPreferenceEntries() { + boolean lteOnlyUnsupported = false; mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId); final boolean display2gOptions = carrierConfig @@ -254,6 +255,7 @@ void setPreferenceEntries() { .addFormat(UiOptions.PresentFormat.addGlobalEntry); break; case ENABLED_NETWORKS_CDMA_NO_LTE_CHOICES: + lteOnlyUnsupported = true; uiOptions = uiOptions .setChoices(R.array.enabled_networks_cdma_no_lte_values) .addFormat(UiOptions.PresentFormat.add3gEntry) @@ -273,6 +275,7 @@ void setPreferenceEntries() { .addFormat(UiOptions.PresentFormat.add2gEntry); break; case ENABLED_NETWORKS_EXCEPT_GSM_LTE_CHOICES: + lteOnlyUnsupported = true; uiOptions = uiOptions .setChoices(R.array.enabled_networks_except_gsm_lte_values) .addFormat(UiOptions.PresentFormat.add3gEntry); @@ -290,6 +293,7 @@ void setPreferenceEntries() { .addFormat(UiOptions.PresentFormat.add3gEntry); break; case ENABLED_NETWORKS_EXCEPT_LTE_CHOICES: + lteOnlyUnsupported = true; uiOptions = uiOptions .setChoices(R.array.enabled_networks_except_lte_values) .addFormat(UiOptions.PresentFormat.add3gEntry) @@ -328,6 +332,11 @@ void setPreferenceEntries() { throw new IllegalArgumentException( uiOptions.getType().name() + " index error."); } + + if (!lteOnlyUnsupported){ + addLteOnlyEntry(); + } + // Compose options based on given values and formats. IntStream.range(0, formatList.size()).forEach(entryIndex -> { switch (formatList.get(entryIndex)) { @@ -506,6 +515,9 @@ void setPreferenceValueAndSummary(int networkMode) { break; } case TelephonyManagerConstants.NETWORK_MODE_LTE_ONLY: + setSummary(mShow4gForLTE + ? R.string.network_4G_only : R.string.network_lte_only); + break; case TelephonyManagerConstants.NETWORK_MODE_LTE_WCDMA: if (!mIsGlobalCdma) { setSelectedEntry( @@ -771,6 +783,16 @@ private void add1xEntry(int value) { mEntriesValue.add(value); } + private void addLteOnlyEntry() { + if (mShow4gForLTE) { + mEntries.add(mContext.getString(R.string.network_4G_only)); + mEntriesValue.add(TelephonyManagerConstants.NETWORK_MODE_LTE_ONLY); + } else { + mEntries.add(mContext.getString(R.string.network_lte_only)); + mEntriesValue.add(TelephonyManagerConstants.NETWORK_MODE_LTE_ONLY); + } + } + private void addCustomEntry(String name, int value) { mEntries.add(name); mEntriesValue.add(value); diff --git a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java index feeed91910c..2746bbe92e0 100644 --- a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java @@ -131,7 +131,7 @@ private int getPreferredNetworkModeSummaryResId(int NetworkMode) { case TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA: return R.string.preferred_network_mode_lte_tdscdma_summary; case TelephonyManagerConstants.NETWORK_MODE_LTE_ONLY: - return R.string.preferred_network_mode_lte_summary; + return R.string.preferred_network_mode_lte_only_summary; case TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_GSM: return R.string.preferred_network_mode_lte_tdscdma_gsm_summary; case TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java index 112debca647..322bb6c229b 100644 --- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java +++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java @@ -16,7 +16,6 @@ package com.android.settings.notification; -import android.app.ActivityThread; import android.app.INotificationManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -30,32 +29,26 @@ import android.os.Message; import android.os.ServiceManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Log; import androidx.lifecycle.OnLifecycleEvent; -import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.Objects; -import java.util.Set; /** - * Update notification volume icon in Settings in response to user adjusting volume. + * Update notification volume icon in Settings in response to user adjusting volume */ public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController { private static final String TAG = "NotificationVolumePreferenceController"; private static final String KEY_NOTIFICATION_VOLUME = "notification_volume"; - private static final boolean CONFIG_DEFAULT_VAL = false; - private boolean mSeparateNotification; private Vibrator mVibrator; private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; @@ -63,74 +56,39 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere private final RingReceiver mReceiver = new RingReceiver(); private final H mHandler = new H(); private INotificationManager mNoMan; + + private int mMuteIcon; private final int mNormalIconId = R.drawable.ic_notifications; private final int mVibrateIconId = R.drawable.ic_volume_ringer_vibrate; private final int mSilentIconId = R.drawable.ic_notifications_off_24dp; + private final boolean mRingNotificationAliased; + + public NotificationVolumePreferenceController(Context context) { this(context, KEY_NOTIFICATION_VOLUME); } public NotificationVolumePreferenceController(Context context, String key) { super(context, key); - mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } + mRingNotificationAliased = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types); updateRingerMode(); } - /** - * Allow for notification slider to be enabled in the scenario where the config switches on - * while settings page is already on the screen by always configuring the preference, even if it - * is currently inactive. - */ - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - if (mPreference == null) { - setupVolPreference(screen); - } - mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - if (mPreference != null) { - mPreference.setVisible(getAvailabilityStatus() == AVAILABLE); - } - updateEffectsSuppressor(); - updatePreferenceIconAndSliderState(); - } - - /** - * Only display the notification slider when the corresponding device config flag is set - */ - private void onDeviceConfigChange(DeviceConfig.Properties properties) { - Set changeSet = properties.getKeyset(); - - if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) { - boolean newVal = properties.getBoolean( - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - if (newVal != mSeparateNotification) { - mSeparateNotification = newVal; - // manually hiding the preference because being unavailable does not do the job - if (mPreference != null) { - mPreference.setVisible(getAvailabilityStatus() == AVAILABLE); - } - } - } - } - - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @Override public void onResume() { super.onResume(); mReceiver.register(true); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - ActivityThread.currentApplication().getMainExecutor(), - this::onDeviceConfigChange); + updateEffectsSuppressor(); + updatePreferenceIconAndSliderState(); } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -138,17 +96,16 @@ public void onResume() { public void onPause() { super.onPause(); mReceiver.register(false); - DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange); } @Override public int getAvailabilityStatus() { - boolean separateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false); + // Show separate notification slider if ring/notification are not aliased by AudioManager -- + // if they are, notification volume is controlled by RingVolumePreferenceController. return mContext.getResources().getBoolean(R.bool.config_show_notification_volume) + && (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext)) && !mHelper.isSingleVolume() - && (separateNotification || !Utils.isVoiceCapable(mContext)) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } diff --git a/src/com/android/settings/notification/RingVolumePreferenceController.java b/src/com/android/settings/notification/RingVolumePreferenceController.java index 7fdb1e16141..a78689f5a0a 100644 --- a/src/com/android/settings/notification/RingVolumePreferenceController.java +++ b/src/com/android/settings/notification/RingVolumePreferenceController.java @@ -16,7 +16,6 @@ package com.android.settings.notification; -import android.app.ActivityThread; import android.app.INotificationManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -30,7 +29,6 @@ import android.os.Message; import android.os.ServiceManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Log; @@ -38,13 +36,11 @@ import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.Objects; -import java.util.Set; /** * This slider can represent both ring and notification, if the corresponding streams are aliased, @@ -63,21 +59,24 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr private int mMuteIcon; - private int mNormalIconId; + /* + * Whether ring and notification streams are aliased together by AudioManager. + * If they are, we'll present one volume control for both. + * If not, we'll present separate volume controls. + */ + private final boolean mRingAliasNotif; + + private final int mNormalIconId; @VisibleForTesting - int mVibrateIconId; + final int mVibrateIconId; @VisibleForTesting - int mSilentIconId; + final int mSilentIconId; @VisibleForTesting - int mTitleId; - - private boolean mSeparateNotification; + final int mTitleId; private INotificationManager mNoMan; - private static final boolean CONFIG_DEFAULT_VAL = false; - public RingVolumePreferenceController(Context context) { this(context, KEY_RING_VOLUME); } @@ -88,56 +87,29 @@ public RingVolumePreferenceController(Context context, String key) { if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } - mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - loadPreferenceIconResources(mSeparateNotification); - updateRingerMode(); - } - private void loadPreferenceIconResources(boolean separateNotification) { - if (separateNotification) { - mTitleId = R.string.separate_ring_volume_option_title; - mNormalIconId = R.drawable.ic_ring_volume; - mSilentIconId = R.drawable.ic_ring_volume_off; - } else { + mRingAliasNotif = isRingAliasNotification(); + if (mRingAliasNotif) { mTitleId = R.string.ring_volume_option_title; + mNormalIconId = R.drawable.ic_notifications; mSilentIconId = R.drawable.ic_notifications_off_24dp; + } else { + mTitleId = R.string.separate_ring_volume_option_title; + + mNormalIconId = R.drawable.ic_ring_volume; + mSilentIconId = R.drawable.ic_ring_volume_off; } // todo: set a distinct vibrate icon for ring vs notification mVibrateIconId = R.drawable.ic_volume_ringer_vibrate; - } - /** - * As the responsibility of this slider changes, so should its title & icon - */ - public void onDeviceConfigChange(DeviceConfig.Properties properties) { - Set changeSet = properties.getKeyset(); - if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) { - boolean valueUpdated = readSeparateNotificationVolumeConfig(); - if (valueUpdated) { - updateEffectsSuppressor(); - selectPreferenceIconState(); - setPreferenceTitle(); - } - } + updateRingerMode(); } - /** - * side effect: updates the cached value of the config, and also the icon - * @return has the config changed? - */ - private boolean readSeparateNotificationVolumeConfig() { - boolean newVal = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL); - - boolean valueUpdated = newVal != mSeparateNotification; - if (valueUpdated) { - mSeparateNotification = newVal; - loadPreferenceIconResources(newVal); - } - - return valueUpdated; + @VisibleForTesting + boolean isRingAliasNotification() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -145,11 +117,8 @@ private boolean readSeparateNotificationVolumeConfig() { public void onResume() { super.onResume(); mReceiver.register(true); - readSeparateNotificationVolumeConfig(); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - ActivityThread.currentApplication().getMainExecutor(), this::onDeviceConfigChange); updateEffectsSuppressor(); - selectPreferenceIconState(); + updatePreferenceIcon(); setPreferenceTitle(); } @@ -158,7 +127,6 @@ public void onResume() { public void onPause() { super.onPause(); mReceiver.register(false); - DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange); } @Override @@ -202,7 +170,7 @@ void updateRingerMode() { final int ringerMode = mHelper.getRingerModeInternal(); if (mRingerMode == ringerMode) return; mRingerMode = ringerMode; - selectPreferenceIconState(); + updatePreferenceIcon(); } private void updateEffectsSuppressor() { @@ -222,8 +190,7 @@ private void updateEffectsSuppressor() { return; } - if (hintsMatch(hints, DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false))) { + if (hintsMatch(hints, mRingAliasNotif)) { mSuppressor = suppressor; if (mPreference != null) { final String text = SuppressorHelper.getSuppressionText(mContext, suppressor); @@ -233,11 +200,11 @@ private void updateEffectsSuppressor() { } @VisibleForTesting - boolean hintsMatch(int hints, boolean notificationSeparated) { + boolean hintsMatch(int hints, boolean ringNotificationAliased) { return (hints & NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS) != 0 || (hints & NotificationListenerService.HINT_HOST_DISABLE_EFFECTS) != 0 || ((hints & NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) - != 0 && !notificationSeparated); + != 0 && ringNotificationAliased); } @VisibleForTesting @@ -250,7 +217,7 @@ void setVibrator(Vibrator vibrator) { mVibrator = vibrator; } - private void selectPreferenceIconState() { + private void updatePreferenceIcon() { if (mPreference != null) { if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { mPreference.showIcon(mNormalIconId); diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java index 0414565721e..d1701599c34 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java @@ -55,17 +55,13 @@ public void setCallback(Callback callback) { public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); if (isAvailable()) { - setupVolPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + mPreference.setCallback(mVolumePreferenceCallback); + mPreference.setStream(getAudioStream()); + mPreference.setMuteIcon(getMuteIcon()); } } - protected void setupVolPreference(PreferenceScreen screen) { - mPreference = screen.findPreference(getPreferenceKey()); - mPreference.setCallback(mVolumePreferenceCallback); - mPreference.setStream(getAudioStream()); - mPreference.setMuteIcon(getMuteIcon()); - } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void onResume() { if (mPreference != null) { diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java index cd9eb2fd44d..48dc2094d50 100644 --- a/src/com/android/settings/password/ChooseLockGenericController.java +++ b/src/com/android/settings/password/ChooseLockGenericController.java @@ -175,8 +175,9 @@ public boolean isScreenLockVisible(ScreenLockType type) { && !managedProfile; // Swipe doesn't make sense for profiles. case MANAGED: return mManagedPasswordProvider.isManagedPasswordChoosable(); - case PIN: case PATTERN: + return false; + case PIN: case PASSWORD: // Hide the secure lock screen options if the device doesn't support the secure lock // screen feature. diff --git a/src/com/android/settings/privacy/ClipboardTimeoutController.java b/src/com/android/settings/privacy/ClipboardTimeoutController.java new file mode 100644 index 00000000000..8207e4e23ca --- /dev/null +++ b/src/com/android/settings/privacy/ClipboardTimeoutController.java @@ -0,0 +1,33 @@ +package com.android.settings.privacy; + +import android.content.Context; +import android.ext.settings.ExtSettings; + +import com.android.settings.ext.IntSettingPrefController; +import com.android.settings.R; + +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; + +public class ClipboardTimeoutController extends IntSettingPrefController { + + public ClipboardTimeoutController(Context ctx, String key) { + super(ctx, key, ExtSettings.CLIPBOARD_AUTO_CLEAR_TIMEOUT); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.switch_off_text, 0); + entries.add(10, SECONDS); + entries.add(30, SECONDS); + entries.add(1, MINUTES); + entries.add(5, MINUTES); + entries.add(10, MINUTES); + entries.add(30, MINUTES); + entries.add(1, HOURS); + entries.add(2, HOURS); + entries.add(4, HOURS); + entries.add(8, HOURS); + } +} diff --git a/src/com/android/settings/security/AutoRebootPrefController.java b/src/com/android/settings/security/AutoRebootPrefController.java new file mode 100644 index 00000000000..0e7b0478981 --- /dev/null +++ b/src/com/android/settings/security/AutoRebootPrefController.java @@ -0,0 +1,34 @@ +package com.android.settings.security; + +import android.content.Context; +import android.ext.settings.ExtSettings; + +import com.android.settings.R; +import com.android.settings.ext.IntSettingPrefController; + +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; + +public class AutoRebootPrefController extends IntSettingPrefController { + + public AutoRebootPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.AUTO_REBOOT_TIMEOUT); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.switch_off_text, 0); + entries.add(10, MINUTES); + entries.add(30, MINUTES); + entries.add(1, HOURS); + entries.add(2, HOURS); + entries.add(4, HOURS); + entries.add(8, HOURS); + entries.add(12, HOURS); + entries.add(1, DAYS); + entries.add(36, HOURS); + entries.add(2, DAYS); + entries.add(3, DAYS); + } +} diff --git a/src/com/android/settings/security/DenyNewUsbPreferenceController.java b/src/com/android/settings/security/DenyNewUsbPreferenceController.java new file mode 100644 index 00000000000..5edddbf63ac --- /dev/null +++ b/src/com/android/settings/security/DenyNewUsbPreferenceController.java @@ -0,0 +1,103 @@ +package com.android.settings.security; + +import android.content.Context; + +import android.os.UserHandle; +import android.os.UserManager; +import android.os.SystemProperties; + +import android.provider.Settings; + + +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class DenyNewUsbPreferenceController extends AbstractPreferenceController + implements PreferenceControllerMixin, OnResume, Preference.OnPreferenceChangeListener { + + private static final String KEY_DENY_NEW_USB = "deny_new_usb"; + private static final String DENY_NEW_USB_PROP = "security.deny_new_usb"; + private static final String DENY_NEW_USB_PERSIST_PROP = "persist.security.deny_new_usb"; + private static final String PREF_KEY_SECURITY_CATEGORY = "security_category"; + + private PreferenceCategory mSecurityCategory; + private ListPreference mDenyNewUsb; + private boolean mIsAdmin; + private final UserManager mUm; + + public DenyNewUsbPreferenceController(Context context) { + super(context); + mUm = UserManager.get(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mSecurityCategory = screen.findPreference(PREF_KEY_SECURITY_CATEGORY); + updatePreferenceState(); + } + + @Override + public boolean isAvailable() { + mIsAdmin = mUm.isAdminUser(); + return mIsAdmin; + } + + @Override + public String getPreferenceKey() { + return KEY_DENY_NEW_USB; + } + + // TODO: should we use onCreatePreferences() instead? + private void updatePreferenceState() { + if (mSecurityCategory == null) { + return; + } + + if (mIsAdmin) { + mDenyNewUsb = (ListPreference) mSecurityCategory.findPreference(KEY_DENY_NEW_USB); + mDenyNewUsb.setValue(SystemProperties.get(DENY_NEW_USB_PERSIST_PROP, "disabled")); + } else { + mSecurityCategory.removePreference(mSecurityCategory.findPreference(KEY_DENY_NEW_USB)); + } + } + + @Override + public void onResume() { + updatePreferenceState(); + + if (mDenyNewUsb != null) { + String mode = mDenyNewUsb.getValue(); + if (mode.equals("dynamic") || mode.equals("disabled")) { + SystemProperties.set(DENY_NEW_USB_PROP, "0"); + } else { + SystemProperties.set(DENY_NEW_USB_PROP, "1"); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + final String key = preference.getKey(); + if (KEY_DENY_NEW_USB.equals(key)) { + String mode = (String) value; + SystemProperties.set(DENY_NEW_USB_PERSIST_PROP, mode); + // The dynamic mode defaults to the disabled state + if (mode.equals("dynamic") || mode.equals("disabled")) { + SystemProperties.set(DENY_NEW_USB_PROP, "0"); + } else { + SystemProperties.set(DENY_NEW_USB_PROP, "1"); + } + } + return true; + } +} diff --git a/src/com/android/settings/security/ExecSpawnPreferenceController.java b/src/com/android/settings/security/ExecSpawnPreferenceController.java new file mode 100644 index 00000000000..78f021210a8 --- /dev/null +++ b/src/com/android/settings/security/ExecSpawnPreferenceController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 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.security; + +import android.content.Context; + +import android.os.UserHandle; +import android.os.UserManager; +import android.os.SystemProperties; + +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import androidx.preference.TwoStatePreference; +import androidx.preference.SwitchPreference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class ExecSpawnPreferenceController extends AbstractPreferenceController + implements PreferenceControllerMixin, OnResume, Preference.OnPreferenceChangeListener { + + private static final String SYS_KEY_EXEC_SPAWN = "persist.security.exec_spawn"; + private static final String PREF_KEY_EXEC_SPAWN = "exec_spawn"; + private static final String PREF_KEY_SECURITY_CATEGORY = "security_category"; + + private PreferenceCategory mSecurityCategory; + private SwitchPreference mExecSpawn; + private boolean mIsAdmin; + private UserManager mUm; + + public ExecSpawnPreferenceController(Context context) { + super(context); + mUm = UserManager.get(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mSecurityCategory = screen.findPreference(PREF_KEY_SECURITY_CATEGORY); + updatePreferenceState(); + } + + @Override + public boolean isAvailable() { + mIsAdmin = mUm.isAdminUser(); + return mIsAdmin; + } + + @Override + public String getPreferenceKey() { + return PREF_KEY_EXEC_SPAWN; + } + + // TODO: should we use onCreatePreferences() instead? + private void updatePreferenceState() { + if (mSecurityCategory == null) { + return; + } + + if (mIsAdmin) { + mExecSpawn = (SwitchPreference) mSecurityCategory.findPreference(PREF_KEY_EXEC_SPAWN); + mExecSpawn.setChecked(SystemProperties.getBoolean(SYS_KEY_EXEC_SPAWN, true)); + } else { + mSecurityCategory.removePreference(mSecurityCategory.findPreference(PREF_KEY_EXEC_SPAWN)); + } + } + + @Override + public void onResume() { + updatePreferenceState(); + if (mExecSpawn != null) { + boolean mode = mExecSpawn.isChecked(); + SystemProperties.set(SYS_KEY_EXEC_SPAWN, Boolean.toString(mode)); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + final String key = preference.getKey(); + if (PREF_KEY_EXEC_SPAWN.equals(key)) { + final boolean mode = !mExecSpawn.isChecked(); + SystemProperties.set(SYS_KEY_EXEC_SPAWN, Boolean.toString(mode)); + } + return true; + } +} diff --git a/src/com/android/settings/security/NativeDebugPreferenceController.java b/src/com/android/settings/security/NativeDebugPreferenceController.java new file mode 100644 index 00000000000..9271e6e21cf --- /dev/null +++ b/src/com/android/settings/security/NativeDebugPreferenceController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.security; + +import android.content.Context; + +import android.os.UserHandle; +import android.os.UserManager; +import android.os.SystemProperties; + +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; +import androidx.preference.TwoStatePreference; +import androidx.preference.SwitchPreference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class NativeDebugPreferenceController extends AbstractPreferenceController + implements PreferenceControllerMixin, OnResume, Preference.OnPreferenceChangeListener { + + private static final String SYS_KEY_NATIVE_DEBUG = "persist.native_debug"; + private static final String PREF_KEY_NATIVE_DEBUG = "native_debug"; + private static final String PREF_KEY_SECURITY_CATEGORY = "security_category"; + + private PreferenceCategory mSecurityCategory; + private SwitchPreference mNativeDebug; + private boolean mIsAdmin; + private UserManager mUm; + + public NativeDebugPreferenceController(Context context) { + super(context); + mUm = UserManager.get(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mSecurityCategory = screen.findPreference(PREF_KEY_SECURITY_CATEGORY); + updatePreferenceState(); + } + + @Override + public boolean isAvailable() { + mIsAdmin = mUm.isAdminUser(); + return mIsAdmin; + } + + @Override + public String getPreferenceKey() { + return PREF_KEY_NATIVE_DEBUG; + } + + // TODO: should we use onCreatePreferences() instead? + private void updatePreferenceState() { + if (mSecurityCategory == null) { + return; + } + + if (mIsAdmin) { + mNativeDebug = (SwitchPreference) mSecurityCategory.findPreference(PREF_KEY_NATIVE_DEBUG); + mNativeDebug.setChecked(SystemProperties.getBoolean(SYS_KEY_NATIVE_DEBUG, true)); + } else { + mSecurityCategory.removePreference(mSecurityCategory.findPreference(PREF_KEY_NATIVE_DEBUG)); + } + } + + @Override + public void onResume() { + updatePreferenceState(); + if (mNativeDebug != null) { + boolean mode = mNativeDebug.isChecked(); + SystemProperties.set(SYS_KEY_NATIVE_DEBUG, Boolean.toString(mode)); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + final String key = preference.getKey(); + if (PREF_KEY_NATIVE_DEBUG.equals(key)) { + final boolean mode = !mNativeDebug.isChecked(); + SystemProperties.set(SYS_KEY_NATIVE_DEBUG, Boolean.toString(mode)); + } + return true; + } +} diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java index b30b54d4d4c..d5146cd42f8 100644 --- a/src/com/android/settings/security/SecuritySettings.java +++ b/src/com/android/settings/security/SecuritySettings.java @@ -105,6 +105,9 @@ private static List buildPreferenceControllers(Con securityPreferenceControllers.add(new CombinedBiometricStatusPreferenceController( context, lifecycle)); securityPreferenceControllers.add(new ChangeScreenLockPreferenceController(context, host)); + securityPreferenceControllers.add(new DenyNewUsbPreferenceController(context)); + securityPreferenceControllers.add(new ExecSpawnPreferenceController(context)); + securityPreferenceControllers.add(new NativeDebugPreferenceController(context)); controllers.add(new PreferenceCategoryController(context, SECURITY_CATEGORY) .setChildren(securityPreferenceControllers)); controllers.addAll(securityPreferenceControllers); diff --git a/src/com/android/settings/users/SendCensoredNotificationsToCurrentUserPreferenceController.java b/src/com/android/settings/users/SendCensoredNotificationsToCurrentUserPreferenceController.java new file mode 100644 index 00000000000..637d7bbb782 --- /dev/null +++ b/src/com/android/settings/users/SendCensoredNotificationsToCurrentUserPreferenceController.java @@ -0,0 +1,60 @@ +package com.android.settings.users; + +import android.content.Context; +import android.provider.Settings; +import android.widget.Toast; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.TogglePreferenceController; + +public class SendCensoredNotificationsToCurrentUserPreferenceController + extends TogglePreferenceController { + private final UserCapabilities mUserCaps; + + public SendCensoredNotificationsToCurrentUserPreferenceController(Context context, String key) { + super(context, key); + mUserCaps = UserCapabilities.create(context); + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + mUserCaps.updateAddUserCapabilities(mContext); + if (!isAvailable()) { + preference.setVisible(false); + } else { + preference.setVisible(mUserCaps.mUserSwitcherEnabled); + } + } + + @Override + public int getAvailabilityStatus() { + return mUserCaps.mUserSwitcherEnabled ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public boolean isChecked() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SEND_CENSORED_NOTIFICATIONS_TO_CURRENT_USER, 0) != 0; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SEND_CENSORED_NOTIFICATIONS_TO_CURRENT_USER, isChecked ? 1 : 0); + } + + @Override + public CharSequence getSummary() { + return mContext.getString( + R.string.user_settings_send_censored_notifications_to_current_summary); + } + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_system; + } +} diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index 3f51b83b9d7..15ed864c601 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -64,6 +64,8 @@ public class UserDetailsSettings extends SettingsPreferenceFragment private static final String KEY_REMOVE_USER = "remove_user"; private static final String KEY_APP_AND_CONTENT_ACCESS = "app_and_content_access"; private static final String KEY_APP_COPYING = "app_copying"; + private static final String KEY_DISALLOW_INSTALL_APPS = "disallow_install_apps"; + private static final String KEY_DISALLOW_INSTALL_APPS_UNKNOWN_SOURCES = "disallow_install_apps_unknown_sources"; /** Integer extra containing the userId to manage */ static final String EXTRA_USER_ID = "user_id"; @@ -76,7 +78,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment private static final int DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER = 6; /** Whether to enable the app_copying fragment. */ - private static final boolean SHOW_APP_COPYING_PREF = false; + private static final boolean SHOW_APP_COPYING_PREF = true; private UserManager mUserManager; private UserCapabilities mUserCaps; @@ -93,6 +95,8 @@ public class UserDetailsSettings extends SettingsPreferenceFragment Preference mAppCopyingPref; @VisibleForTesting Preference mRemoveUserPref; + private SwitchPreference mInstallAppsPref; + private SwitchPreference mInstallAppsUnknownSourcesPref; @VisibleForTesting /** The user being studied (not the user doing the studying). */ @@ -165,12 +169,60 @@ public boolean onPreferenceClick(Preference preference) { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - if (Boolean.TRUE.equals(newValue)) { - showDialog(mUserInfo.isGuest() ? DIALOG_CONFIRM_ENABLE_CALLING - : DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS); - return false; + if (preference == mPhonePref) { + if (Boolean.TRUE.equals(newValue)) { + showDialog(mUserInfo.isGuest() ? DIALOG_CONFIRM_ENABLE_CALLING + : DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS); + return false; + } + enableCallsAndSms(false); + } else if (preference == mInstallAppsPref) { + if (mUserInfo.isGuest()) { + mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, (Boolean) newValue); + mUserManager.setDefaultGuestRestrictions(mDefaultGuestRestrictions); + + // Update the guest's restrictions, if there is a guest + // TODO: Maybe setDefaultGuestRestrictions() can internally just set the restrictions + // on any existing guest rather than do it here with multiple Binder calls. + List users = mUserManager.getUsers(true); + for (UserInfo user: users) { + if (user.isGuest()) { + UserHandle userHandle = UserHandle.of(user.id); + for (String key : mDefaultGuestRestrictions.keySet()) { + mUserManager.setUserRestriction( + key, mDefaultGuestRestrictions.getBoolean(key), userHandle); + } + } + } + } else { + UserHandle userHandle = UserHandle.of(mUserInfo.id); + mUserManager.setUserRestriction(UserManager.DISALLOW_INSTALL_APPS, (Boolean) newValue, + userHandle); + } + } else if (preference == mInstallAppsUnknownSourcesPref) { + if (mUserInfo.isGuest()) { + mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, (Boolean) newValue); + mUserManager.setDefaultGuestRestrictions(mDefaultGuestRestrictions); + + // Update the guest's restrictions, if there is a guest + // TODO: Maybe setDefaultGuestRestrictions() can internally just set the restrictions + // on any existing guest rather than do it here with multiple Binder calls. + List users = mUserManager.getUsers(true); + for (UserInfo user: users) { + if (user.isGuest()) { + UserHandle userHandle = UserHandle.of(user.id); + for (String key : mDefaultGuestRestrictions.keySet()) { + mUserManager.setUserRestriction( + key, mDefaultGuestRestrictions.getBoolean(key), userHandle); + } + } + } + } else { + UserHandle userHandle = UserHandle.of(mUserInfo.id); + mUserManager.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, (Boolean) newValue, + userHandle); + } } - enableCallsAndSms(false); return true; } @@ -273,6 +325,8 @@ void initialize(Context context, Bundle arguments) { mRemoveUserPref = findPreference(KEY_REMOVE_USER); mAppAndContentAccessPref = findPreference(KEY_APP_AND_CONTENT_ACCESS); mAppCopyingPref = findPreference(KEY_APP_COPYING); + mInstallAppsPref = findPreference(KEY_DISALLOW_INSTALL_APPS); + mInstallAppsUnknownSourcesPref = findPreference(KEY_DISALLOW_INSTALL_APPS_UNKNOWN_SOURCES); mSwitchUserPref.setTitle( context.getString(com.android.settingslib.R.string.user_switch_to_user, @@ -291,6 +345,8 @@ void initialize(Context context, Bundle arguments) { removePreference(KEY_REMOVE_USER); removePreference(KEY_APP_AND_CONTENT_ACCESS); removePreference(KEY_APP_COPYING); + removePreference(KEY_DISALLOW_INSTALL_APPS); + removePreference(KEY_DISALLOW_INSTALL_APPS_UNKNOWN_SOURCES); } else { if (!Utils.isVoiceCapable(context)) { // no telephony removePreference(KEY_ENABLE_TELEPHONY); @@ -324,11 +380,16 @@ void initialize(Context context, Bundle arguments) { if (!SHOW_APP_COPYING_PREF) { removePreference(KEY_APP_COPYING); } + removePreference(KEY_DISALLOW_INSTALL_APPS); + removePreference(KEY_DISALLOW_INSTALL_APPS_UNKNOWN_SOURCES); } else { mPhonePref.setChecked(!mUserManager.hasUserRestriction( UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); mRemoveUserPref.setTitle(R.string.user_remove_user); - removePreference(KEY_APP_COPYING); + mInstallAppsPref.setChecked(mUserManager.hasUserRestriction( + UserManager.DISALLOW_INSTALL_APPS, new UserHandle(userId))); + mInstallAppsUnknownSourcesPref.setChecked(mUserManager.hasUserRestriction( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, new UserHandle(userId))); } if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) { @@ -339,6 +400,8 @@ void initialize(Context context, Bundle arguments) { mPhonePref.setOnPreferenceChangeListener(this); mAppAndContentAccessPref.setOnPreferenceClickListener(this); mAppCopyingPref.setOnPreferenceClickListener(this); + mInstallAppsPref.setOnPreferenceChangeListener(this); + mInstallAppsUnknownSourcesPref.setOnPreferenceChangeListener(this); } } diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index f1ee56ccef8..a18ffce3808 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -131,6 +131,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final String KEY_GUEST_EXIT = "guest_exit"; private static final String KEY_REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit"; private static final String KEY_GUEST_USER_CATEGORY = "guest_user_category"; + private static final String KEY_SEND_CENSORED_NOTIFICATIONS = + "user_settings_send_censored_notifications_to_current"; private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in"; @@ -212,6 +214,7 @@ public class UserSettings extends SettingsPreferenceFragment new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY); private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; private RemoveGuestOnExitPreferenceController mRemoveGuestOnExitPreferenceController; + private SendCensoredNotificationsToCurrentUserPreferenceController mSendCensoredNotificationsToCurrentUserPreferenceController; private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController; private TimeoutToDockUserPreferenceController mTimeoutToDockUserPreferenceController; private UserCreatingDialog mUserCreatingDialog; @@ -303,6 +306,10 @@ public void onCreate(Bundle icicle) { mRemoveGuestOnExitPreferenceController = new RemoveGuestOnExitPreferenceController( activity, KEY_REMOVE_GUEST_ON_EXIT, this, mHandler); + mSendCensoredNotificationsToCurrentUserPreferenceController = + new SendCensoredNotificationsToCurrentUserPreferenceController(activity, + KEY_SEND_CENSORED_NOTIFICATIONS); + mMultiUserTopIntroPreferenceController = new MultiUserTopIntroPreferenceController(activity, KEY_MULTIUSER_TOP_INTRO); @@ -312,11 +319,14 @@ public void onCreate(Bundle icicle) { final PreferenceScreen screen = getPreferenceScreen(); mAddUserWhenLockedPreferenceController.displayPreference(screen); mRemoveGuestOnExitPreferenceController.displayPreference(screen); + mSendCensoredNotificationsToCurrentUserPreferenceController.displayPreference(screen); mMultiUserTopIntroPreferenceController.displayPreference(screen); mTimeoutToDockUserPreferenceController.displayPreference(screen); screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey()) .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController); + screen.findPreference(mSendCensoredNotificationsToCurrentUserPreferenceController.getPreferenceKey()) + .setOnPreferenceChangeListener(mSendCensoredNotificationsToCurrentUserPreferenceController); screen.findPreference(mRemoveGuestOnExitPreferenceController.getPreferenceKey()) .setOnPreferenceChangeListener(mRemoveGuestOnExitPreferenceController); @@ -391,6 +401,9 @@ public void onResume() { mTimeoutToDockUserPreferenceController.getPreferenceKey())); mRemoveGuestOnExitPreferenceController.updateState(screen.findPreference( mRemoveGuestOnExitPreferenceController.getPreferenceKey())); + mSendCensoredNotificationsToCurrentUserPreferenceController.updateState(screen.findPreference( + mSendCensoredNotificationsToCurrentUserPreferenceController.getPreferenceKey())); + if (mShouldUpdateUserList) { updateUI(); } @@ -1287,8 +1300,13 @@ void updateUserList() { mAddUserWhenLockedPreferenceController.getPreferenceKey()); mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen); + final Preference sendCensoredNotifs = getPreferenceScreen().findPreference( + mSendCensoredNotificationsToCurrentUserPreferenceController.getPreferenceKey()); + final Preference multiUserTopIntroPrefence = getPreferenceScreen().findPreference( mMultiUserTopIntroPreferenceController.getPreferenceKey()); + + mSendCensoredNotificationsToCurrentUserPreferenceController.updateState(sendCensoredNotifs); mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPrefence); mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled); updateGuestPreferences(); @@ -1703,6 +1721,8 @@ public List getNonIndexableKeysFromXml(Context context, int xmlResId, new AddUserWhenLockedPreferenceController( context, KEY_ADD_USER_WHEN_LOCKED); controller.updateNonIndexableKeys(niks); + new SendCensoredNotificationsToCurrentUserPreferenceController(context, + KEY_SEND_CENSORED_NOTIFICATIONS).updateNonIndexableKeys(niks); new AutoSyncDataPreferenceController(context, null /* parent */) .updateNonIndexableKeys(niks); new AutoSyncPersonalDataPreferenceController(context, null /* parent */) diff --git a/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java new file mode 100644 index 00000000000..dfd08782366 --- /dev/null +++ b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.wifi; + +import android.content.Context; +import android.net.wifi.WifiManager; +import android.provider.Settings; +import android.util.Log; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; + +public class WifiTimeoutPreferenceController extends BasePreferenceController implements + PreferenceControllerMixin, Preference.OnPreferenceChangeListener { + private static final String TAG = "WifiTimeoutPrefCtrl"; + + public static final int FALLBACK_WIFI_TIMEOUT_VALUE = 0; + + private final String mWifiTimeoutKey; + + protected WifiManager mWifiManager; + + public WifiTimeoutPreferenceController(Context context, String key) { + super(context, key); + mWifiTimeoutKey = key; + + mWifiManager = context.getSystemService(WifiManager.class); + if (mWifiManager == null) { + Log.e(TAG, "Wifi is not supported on this device"); + return; + } + } + + @Override + public int getAvailabilityStatus() { + return mWifiManager != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return mWifiTimeoutKey; + } + + @Override + public void updateState(Preference preference) { + final ListPreference timeoutListPreference = (ListPreference) preference; + final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.WIFI_OFF_TIMEOUT, FALLBACK_WIFI_TIMEOUT_VALUE); + timeoutListPreference.setValue(String.valueOf(currentTimeout)); + updateTimeoutPreferenceDescription(timeoutListPreference, + Long.parseLong(timeoutListPreference.getValue())); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + try { + long value = Long.parseLong((String) newValue); + Settings.Global.putLong(mContext.getContentResolver(), Settings.Global.WIFI_OFF_TIMEOUT, value); + updateTimeoutPreferenceDescription((ListPreference) preference, value); + } catch (NumberFormatException e) { + Log.e(TAG, "could not persist wifi timeout setting", e); + } + return true; + } + + public static CharSequence getTimeoutDescription( + long currentTimeout, CharSequence[] entries, CharSequence[] values) { + if (currentTimeout < 0 || entries == null || values == null + || values.length != entries.length) { + return null; + } + + for (int i = 0; i < values.length; i++) { + long timeout = Long.parseLong(values[i].toString()); + if (currentTimeout == timeout) { + return entries[i]; + } + } + return null; + } + + private void updateTimeoutPreferenceDescription(ListPreference preference, + long currentTimeout) { + final CharSequence[] entries = preference.getEntries(); + final CharSequence[] values = preference.getEntryValues(); + final CharSequence timeoutDescription = getTimeoutDescription( + currentTimeout, entries, values); + String summary = ""; + if (timeoutDescription != null) { + if (currentTimeout != 0) + summary = mContext.getString(R.string.wifi_timeout_summary1, timeoutDescription); + else + summary = mContext.getString(R.string.wifi_timeout_summary2); + } + preference.setSummary(summary); + } +} diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java index f7fc07a90b0..c9673777270 100644 --- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java +++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java @@ -787,7 +787,7 @@ private void refreshWifiType() { } private int getMacAddressTitle() { - if (mWifiEntry.getPrivacy() == WifiEntry.PRIVACY_RANDOMIZED_MAC) { + if (mWifiEntry.getPrivacy() != WifiEntry.PRIVACY_DEVICE_MAC) { return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED ? R.string.wifi_advanced_randomized_mac_address_title : R.string.wifi_advanced_randomized_mac_address_disconnected_title; diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java index 632a5624c4d..d695b8a825a 100644 --- a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java +++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java @@ -41,6 +41,10 @@ public class WifiPrivacyPreferenceController2 extends BasePreferenceController i private WifiEntry mWifiEntry; private Preference mPreference; + private static final int PREF_RANDOMIZATION_ALWAYS = 0; + private static final int PREF_RANDOMIZATION_PERSISTENT = 1; + private static final int PREF_RANDOMIZATION_NONE = 2; + public WifiPrivacyPreferenceController2(Context context) { super(context, KEY_WIFI_PRIVACY); @@ -98,8 +102,6 @@ int getRandomizationValue() { return mWifiEntry.getPrivacy(); } - private static final int PREF_RANDOMIZATION_PERSISTENT = 0; - private static final int PREF_RANDOMIZATION_NONE = 1; /** * Returns preference index value. @@ -108,8 +110,14 @@ int getRandomizationValue() { * @return index value of preference */ public static int translateMacRandomizedValueToPrefValue(int macRandomized) { - return (macRandomized == WifiEntry.PRIVACY_RANDOMIZED_MAC) - ? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE; + switch (macRandomized) { + case WifiEntry.PRIVACY_RANDOMIZED_MAC: + return PREF_RANDOMIZATION_PERSISTENT; + case WifiEntry.PRIVACY_DEVICE_MAC: + return PREF_RANDOMIZATION_NONE; + default: + return PREF_RANDOMIZATION_ALWAYS; + } } /** @@ -119,8 +127,14 @@ public static int translateMacRandomizedValueToPrefValue(int macRandomized) { * @return mac randomized value */ public static int translatePrefValueToMacRandomizedValue(int prefMacRandomized) { - return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT) - ? WifiEntry.PRIVACY_RANDOMIZED_MAC : WifiEntry.PRIVACY_DEVICE_MAC; + switch (prefMacRandomized) { + case PREF_RANDOMIZATION_PERSISTENT: + return WifiEntry.PRIVACY_RANDOMIZED_MAC; + case PREF_RANDOMIZATION_NONE: + return WifiEntry.PRIVACY_DEVICE_MAC; + default: + return WifiEntry.PRIVACY_RANDOMIZATION_ALWAYS; + } } private void updateSummary(ListPreference preference, int macRandomized) { @@ -150,6 +164,8 @@ private int getWifiEntryPrivacy(WifiConfiguration wifiConfiguration) { return WifiEntry.PRIVACY_DEVICE_MAC; case WifiConfiguration.RANDOMIZATION_PERSISTENT: return WifiEntry.PRIVACY_RANDOMIZED_MAC; + case WifiConfiguration.RANDOMIZATION_ALWAYS: + return WifiEntry.PRIVACY_RANDOMIZATION_ALWAYS; default: return WifiEntry.PRIVACY_UNKNOWN; } diff --git a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java index 7e7ad10d8c1..96b9e6219c8 100644 --- a/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/NotificationVolumePreferenceControllerTest.java @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -26,17 +25,10 @@ import android.content.res.Resources; import android.media.AudioManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.telephony.TelephonyManager; -import androidx.preference.PreferenceManager; -import androidx.preference.PreferenceScreen; -import androidx.test.core.app.ApplicationProvider; - -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; -import com.android.settings.core.BasePreferenceController; -import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.internal.R; import org.junit.Before; import org.junit.Test; @@ -45,12 +37,11 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowDeviceConfig.class}) public class NotificationVolumePreferenceControllerTest { + @Mock private AudioHelper mHelper; @Mock @@ -61,11 +52,6 @@ public class NotificationVolumePreferenceControllerTest { private Vibrator mVibrator; @Mock private Resources mResources; - @Mock - private PreferenceManager mPreferenceManager; - - private static final String READ_DEVICE_CONFIG_PERMISSION = - "android.permission.READ_DEVICE_CONFIG"; private Context mContext; private NotificationVolumePreferenceController mController; @@ -101,9 +87,7 @@ public void isAvailable_singleVolume_shouldReturnFalse() { public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() { when(mResources.getBoolean( com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); + when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(true); NotificationVolumePreferenceController controller = new NotificationVolumePreferenceController(mContext); @@ -121,9 +105,7 @@ public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() { public void isAvailable_voiceCapable_separatedFromRing_shouldReturnTrue() { when(mResources.getBoolean( com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); + when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(false); NotificationVolumePreferenceController controller = new NotificationVolumePreferenceController(mContext); @@ -188,70 +170,4 @@ public void setHintNotification_Matches() { .isTrue(); } - @Test - public void enableSeparateNotificationConfig_controllerBecomesAvailable() { - PreferenceScreen screen = spy(new PreferenceScreen(mContext, null)); - VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class); - when(screen.getPreferenceManager()).thenReturn(mPreferenceManager); - when(screen.getContext()).thenReturn(mContext); - when(mResources.getBoolean( - com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - // block the alternative condition to enable controller - when(mTelephonyManager.isVoiceCapable()).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - - NotificationVolumePreferenceController controller = - new NotificationVolumePreferenceController(mContext); - when(screen.findPreference(controller.getPreferenceKey())) - .thenReturn(volumeSeekBarPreference); - - // allow the controller to subscribe - Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext()) - .grantPermissions(READ_DEVICE_CONFIG_PERMISSION); - controller.onResume(); - controller.displayPreference(screen); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, Boolean.toString(true), - false); - - assertThat(controller.getAvailabilityStatus() - == BasePreferenceController.AVAILABLE).isTrue(); - } - - @Test - public void disableSeparateNotificationConfig_controllerBecomesUnavailable() { - PreferenceScreen screen = spy(new PreferenceScreen(mContext, null)); - VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class); - when(screen.getPreferenceManager()).thenReturn(mPreferenceManager); - when(screen.getContext()).thenReturn(mContext); - when(mResources.getBoolean( - com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true); - - // block the alternative condition to enable controller - when(mTelephonyManager.isVoiceCapable()).thenReturn(true); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); - - NotificationVolumePreferenceController controller = - new NotificationVolumePreferenceController(mContext); - - when(screen.findPreference(controller.getPreferenceKey())) - .thenReturn(volumeSeekBarPreference); - - Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext()) - .grantPermissions(READ_DEVICE_CONFIG_PERMISSION); - controller.onResume(); - controller.displayPreference(screen); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - - assertThat(controller.getAvailabilityStatus() - == BasePreferenceController.UNSUPPORTED_ON_DEVICE).isTrue(); - } - } diff --git a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java index 1ad26c71546..02757d52874 100644 --- a/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/RingVolumePreferenceControllerTest.java @@ -27,13 +27,10 @@ import android.content.res.Resources; import android.media.AudioManager; import android.os.Vibrator; -import android.provider.DeviceConfig; import android.service.notification.NotificationListenerService; import android.telephony.TelephonyManager; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.settings.R; -import com.android.settings.testutils.shadow.ShadowDeviceConfig; import org.junit.Before; import org.junit.Test; @@ -42,11 +39,9 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowDeviceConfig.class}) public class RingVolumePreferenceControllerTest { @Mock @@ -129,10 +124,9 @@ public void isPublicSlice_returnTrue() { // todo: verify that the title change is displayed, by examining the underlying preference @Test public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() { - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); - + when(mResources.getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types)) + .thenReturn(false); final RingVolumePreferenceController controller = new RingVolumePreferenceController(mContext); @@ -144,9 +138,8 @@ public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() { @Test public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotification() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); - + when(mResources.getBoolean( + com.android.internal.R.bool.config_alias_ring_notif_stream_types)).thenReturn(true); final RingVolumePreferenceController control = new RingVolumePreferenceController(mContext); int expectedTitleId = R.string.ring_volume_option_title; @@ -157,39 +150,39 @@ public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotificati @Test public void setHintsRing_aliased_Matches() { assertThat(mController.hintsMatch( - NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue(); + NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue(); } @Test public void setHintsRingNotification_aliased_Matches() { assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS, - false)).isTrue(); + true)).isTrue(); } @Test public void setHintNotification_aliased_Matches() { assertThat(mController .hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS, - false)).isTrue(); + true)).isTrue(); } @Test public void setHintsRing_unaliased_Matches() { assertThat(mController.hintsMatch( - NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue(); + NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue(); } @Test public void setHintsRingNotification_unaliased_Matches() { assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS, - true)).isTrue(); + false)).isTrue(); } @Test public void setHintNotification_unaliased_doesNotMatch() { assertThat(mController .hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS, - true)).isFalse(); + false)).isFalse(); } @Test