From b02340b69c283879696bc647dc1f7f4af83fc3be Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Fri, 15 Nov 2024 08:18:09 +0200 Subject: [PATCH 001/128] add isLoggable check to overly verbose PrefCtrlListHelper log statement --- .../android/settings/core/PreferenceControllerListHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/core/PreferenceControllerListHelper.java b/src/com/android/settings/core/PreferenceControllerListHelper.java index dea8c971c49..9dc0a9fa108 100644 --- a/src/com/android/settings/core/PreferenceControllerListHelper.java +++ b/src/com/android/settings/core/PreferenceControllerListHelper.java @@ -74,7 +74,9 @@ public static List getPreferenceControllersFromXml(Con try { controller = BasePreferenceController.createInstance(context, controllerName); } catch (IllegalStateException e) { - Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName); + } final String key = metadata.getString(METADATA_KEY); final boolean isWorkProfile = metadata.getBoolean(METADATA_FOR_WORK, false); if (TextUtils.isEmpty(key)) { From 3b8336ec43bbb6b4576316f71b9698a8ab7a2ea1 Mon Sep 17 00:00:00 2001 From: flawedworld Date: Sat, 5 Feb 2022 17:42:07 +0000 Subject: [PATCH 002/128] add GrapheneOS icon --- AndroidManifest.xml | 3 ++- res/drawable/ic_launcher_foreground.xml | 30 +++++++++++++++++++++++++ res/mipmap-anydpi/ic_launcher.xml | 6 +++++ res/mipmap-anydpi/ic_launcher_round.xml | 6 +++++ res/values/ic_launcher_background.xml | 4 ++++ 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 res/drawable/ic_launcher_foreground.xml create mode 100644 res/mipmap-anydpi/ic_launcher.xml create mode 100644 res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 res/values/ic_launcher_background.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2295ee3dd1a..9cf39de9e6d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -152,7 +152,8 @@ + + + + + diff --git a/res/mipmap-anydpi/ic_launcher.xml b/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 00000000000..5c84730caa7 --- /dev/null +++ b/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + 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/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 + From 51e0905444f0d4c97c4d737a395db6bf68b8e538 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Fri, 14 Oct 2022 21:08:02 -0400 Subject: [PATCH 003/128] set styles/wallpaper picker action name --- res/values/config.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 95f8eba8f84..4d191a97cf3 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -53,9 +53,9 @@ - + android.intent.action.MAIN - + android.intent.action.MAIN com.android.wallpaper.LAUNCH_SOURCE From 18265e642d47767ab9f30def31132496714a1bac Mon Sep 17 00:00:00 2001 From: Danny Lin Date: Sat, 17 Apr 2021 15:15:51 +0100 Subject: [PATCH 004/128] Settings: Integrate ThemePicker and WallpaperPicker2 This uses the standalone wallpaper picker class from WallpaperPicker2 as seen in the Pixel stock ROM. --- res/values/config.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 4d191a97cf3..8703c363d14 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -48,10 +48,10 @@ - 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 From 9d00ffee31fa146a93a8351266f10b1ddbf1c6c0 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Tue, 16 Feb 2021 02:22:41 -0500 Subject: [PATCH 005/128] disable showing wallpaper attribution --- res/values/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 8703c363d14..7e8288936b4 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -478,7 +478,7 @@ true - true + false true From 39f92239c96388b9829c2145fb5ee40e187f2d9a Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 10 Mar 2021 00:45:12 -0500 Subject: [PATCH 006/128] add TalkBack accessibility service --- res/values/config.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/res/values/config.xml b/res/values/config.xml index 7e8288936b4..465b819f577 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -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 From 8bf96250a04d9dfb1d8e54818974869f4c536f42 Mon Sep 17 00:00:00 2001 From: inthewaves Date: Sun, 20 Sep 2020 17:57:30 -0700 Subject: [PATCH 007/128] get outer NFC preference to listen for changes The NFC preference in Settings -> Connected devices -> Connection preferences doesn't listen to changes from the NFC tile. (This wasn't an issue in Android 10, because there used to be a SwitchPreference that listened there.) --- .../NfcAndPaymentFragmentController.java | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) 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; + } } From 5b93ef0520a71f1f7e9b153ca9954a2463f37227 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Tue, 8 Oct 2019 08:11:55 -0400 Subject: [PATCH 008/128] disable legacy suggestions --- res/values/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 465b819f577..4db12c329de 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -240,7 +240,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 From dc78e39bb85c2fb48e87018c1cb7c8633531664a Mon Sep 17 00:00:00 2001 From: Renlord Date: Sat, 11 Apr 2020 20:04:51 +1000 Subject: [PATCH 009/128] add missing android title for top_level_settings --- res/xml/top_level_settings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml index 44fe7fcc3d3..fbbe0b28814 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"> Date: Sun, 16 May 2021 00:51:15 +0100 Subject: [PATCH 010/128] Remove misleading "Google Play system update" info field --- res/xml/firmware_version.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/res/xml/firmware_version.xml b/res/xml/firmware_version.xml index 41f7733cfb2..edc910b36cf 100644 --- a/res/xml/firmware_version.xml +++ b/res/xml/firmware_version.xml @@ -37,14 +37,6 @@ settings:enableCopying="true" settings:controller="com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelPreferenceController"/> - - - Date: Sun, 28 May 2023 18:42:41 +0300 Subject: [PATCH 011/128] extend list of system apps that are not allowed to be disabled --- .../ApplicationFeatureProviderImpl.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index 62c5910904a..ca33d2264c4 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -22,12 +22,14 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; +import android.ext.PackageId; import android.location.LocationManager; import android.os.RemoteException; import android.os.SystemConfigManager; @@ -201,6 +203,39 @@ private Set getEnabledPackageAllowlist() { if (mPm.getWellbeingPackageName() != null) { keepEnabledPackages.add(mPm.getWellbeingPackageName()); } + + ArraySet systemPkgs = new ArraySet<>(new String[] { + // Bundled keyboard, needed for text input in Direct Boot mode if the selected 3rd + // party keyboard doesn't support it + "com.android.inputmethod.latin", + + // Replacing WebView is not supported + "app.vanadium.webview", + + // Only bundled camera can handle some of camera intents + "app.grapheneos.camera", + + // Disabling GmsCompat app breaks the GmsCompat layer + com.android.internal.gmscompat.GmsCompatApp.PKG_NAME, + + // EuiccSupportPixel handles firmware updates of embedded secure element that is + // used for eSIM, NFC, Felica etc + PackageId.EUICC_SUPPORT_PIXEL_NAME, + + // CameraX extensions break when it's disabled, which breaks apps that use the + // CameraX library + PackageId.PIXEL_CAMERA_SERVICES_NAME, + }); + + PackageManager pm = mContext.getPackageManager(); + + for (ApplicationInfo ai : pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY)) { + String pkgName = ai.packageName; + if (systemPkgs.contains(pkgName)) { + keepEnabledPackages.add(pkgName); + } + } + return keepEnabledPackages; } From f25eb10162e36d8f548cb917475a3655952a7279 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sun, 12 Feb 2023 20:21:09 +0200 Subject: [PATCH 012/128] add a separate file for non-AOSP strings --- res/values/strings_ext.xml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 res/values/strings_ext.xml diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml new file mode 100644 index 00000000000..6da1fca2c7a --- /dev/null +++ b/res/values/strings_ext.xml @@ -0,0 +1,6 @@ + + + + + + From 3dbeb437be8ff5b03c9697399d82c3acd6277566 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Thu, 6 Apr 2023 12:06:11 +0300 Subject: [PATCH 013/128] add a set of base classes for creating Fragment-based preference UI --- res/values/strings_ext.xml | 4 +- .../settings/ext/BoolSettingFragment.java | 157 ++++++++++++++++++ .../BoolSettingFragmentPrefController.java | 26 +++ .../ext/ExtSettingFragmentPrefController.java | 42 +++++ .../settings/ext/FragmentPrefController.java | 36 ++++ 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/ext/BoolSettingFragment.java create mode 100644 src/com/android/settings/ext/BoolSettingFragmentPrefController.java create mode 100644 src/com/android/settings/ext/ExtSettingFragmentPrefController.java create mode 100644 src/com/android/settings/ext/FragmentPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 6da1fca2c7a..f249f7fa627 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -1,6 +1,8 @@ - + Enable + Enabled + Disabled diff --git a/src/com/android/settings/ext/BoolSettingFragment.java b/src/com/android/settings/ext/BoolSettingFragment.java new file mode 100644 index 00000000000..e342faa688d --- /dev/null +++ b/src/com/android/settings/ext/BoolSettingFragment.java @@ -0,0 +1,157 @@ +package com.android.settings.ext; + +import android.content.Context; +import android.content.Intent; +import android.ext.settings.BoolSetting; +import android.net.Uri; +import android.os.Bundle; + +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreferenceCompat; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.widget.FooterPreference; + +public abstract class BoolSettingFragment extends DashboardFragment implements ExtSettingPrefController { + + private static final String TAG = BoolSettingFragment.class.getSimpleName(); + + protected SwitchPreferenceCompat mainSwitch; + protected boolean invertSetting; + + private ExtSettingControllerHelper helper; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + helper = new ExtSettingControllerHelper<>(requireContext(), getSetting()); + + getActivity().setTitle(getTitle()); + + Context ctx = requireContext(); + + PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(ctx); + + var mainSwitch = new SwitchPreferenceCompat(ctx); + mainSwitch.setTitle(getMainSwitchTitle()); + + this.mainSwitch = mainSwitch; + refreshMainSwitch(); + + mainSwitch.setOnPreferenceChangeListener((preference, newValue) -> { + boolean state = (boolean) newValue; + + if (invertSetting) { + state = !state; + } + + if (interceptMainSwitchChange(state)) { + return false; + } + + if (!getSetting().put(requireContext(), state)) { + return false; + } + + onMainSwitchChanged(state); + + return true; + }); + + screen.addPreference(mainSwitch); + + addExtraPrefs(screen); + + FooterPreference footer = makeFooterPref(new FooterPreference.Builder(ctx)); + + if (footer != null) { + screen.addPreference(footer); + } + + setPreferenceScreen(screen); + } + + protected abstract BoolSetting getSetting(); + + protected abstract CharSequence getTitle(); + + protected CharSequence getMainSwitchTitle() { + return getText(R.string.bool_setting_enable); + } + + protected CharSequence getMainSwitchSummary() { + return null; + } + + protected void addExtraPrefs(PreferenceScreen screen) {} + + protected FooterPreference makeFooterPref(FooterPreference.Builder builder) { + return null; + } + + protected static void setFooterPrefLearnMoreUri(FooterPreference p, Uri uri) { + p.setLearnMoreAction(v -> { + var intent = new Intent(Intent.ACTION_VIEW, uri); + p.getContext().startActivity(intent); + }); + } + + protected boolean interceptMainSwitchChange(boolean newValue) { + return false; + } + + protected void onMainSwitchChanged(boolean state) {} + + protected void refreshMainSwitch() { + boolean state = getSetting().get(requireContext()); + if (invertSetting) { + state = !state; + } + mainSwitch.setChecked(state); + + CharSequence mainSwitchSummary = getMainSwitchSummary(); + if (mainSwitchSummary != null) { + mainSwitch.setSummary(mainSwitchSummary); + } + } + + @Override + public void onResume() { + super.onResume(); + helper.onResume(this); + refreshMainSwitch(); + } + + @Override + public void onPause() { + super.onPause(); + + helper.onPause(this); + } + + @Override + public void accept(BoolSetting setting) { + refreshMainSwitch(); + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + + @Override + protected int getPreferenceScreenResId() { + return 0; + } + + @Override + protected String getLogTag() { + return TAG; + } + + protected final CharSequence resText(int res) { + return requireContext().getText(res); + } +} diff --git a/src/com/android/settings/ext/BoolSettingFragmentPrefController.java b/src/com/android/settings/ext/BoolSettingFragmentPrefController.java new file mode 100644 index 00000000000..3f9223b800a --- /dev/null +++ b/src/com/android/settings/ext/BoolSettingFragmentPrefController.java @@ -0,0 +1,26 @@ +package com.android.settings.ext; + +import android.content.Context; +import android.ext.settings.BoolSetting; + +import com.android.settings.R; + +public abstract class BoolSettingFragmentPrefController extends ExtSettingFragmentPrefController { + + protected BoolSettingFragmentPrefController(Context ctx, String key, BoolSetting setting) { + super(ctx, key, setting); + } + + @Override + public CharSequence getSummary() { + return setting.get(mContext) ? getSummaryOn() : getSummaryOff(); + } + + protected CharSequence getSummaryOn() { + return resText(R.string.bool_setting_enabled); + } + + protected CharSequence getSummaryOff() { + return resText(R.string.bool_setting_disabled); + } +} diff --git a/src/com/android/settings/ext/ExtSettingFragmentPrefController.java b/src/com/android/settings/ext/ExtSettingFragmentPrefController.java new file mode 100644 index 00000000000..2d87635e0d2 --- /dev/null +++ b/src/com/android/settings/ext/ExtSettingFragmentPrefController.java @@ -0,0 +1,42 @@ +package com.android.settings.ext; + +import android.content.Context; +import android.ext.settings.Setting; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; + +public abstract class ExtSettingFragmentPrefController extends FragmentPrefController + implements ExtSettingPrefController { + protected final T setting; + protected final ExtSettingControllerHelper helper; + + protected ExtSettingFragmentPrefController(Context ctx, String key, T setting) { + super(ctx, key); + this.setting = setting; + helper = new ExtSettingControllerHelper(ctx, setting); + } + + @Override + public int getAvailabilityStatus() { + return helper.getAvailabilityStatus(); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + helper.onResume(this); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + helper.onPause(this); + } + + // called by the setting observer + @Override + public void accept(T setting) { + if (preference != null) { + updateState(preference); + } + } +} diff --git a/src/com/android/settings/ext/FragmentPrefController.java b/src/com/android/settings/ext/FragmentPrefController.java new file mode 100644 index 00000000000..b09ab9615c2 --- /dev/null +++ b/src/com/android/settings/ext/FragmentPrefController.java @@ -0,0 +1,36 @@ +package com.android.settings.ext; + +import android.content.Context; + +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +import com.android.settings.core.BasePreferenceController; + +public abstract class FragmentPrefController + extends BasePreferenceController { + + protected FragmentPrefController(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; + } + } + + protected final CharSequence resText(int res) { + return mContext.getText(res); + } +} From 3776d83c8d984f9dea7e331328ccf718389eea89 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sun, 12 Feb 2023 19:43:44 +0200 Subject: [PATCH 014/128] add base class for ListPreference controllers --- .../ext/AbstractListPreferenceController.java | 242 ++++++++++++++++++ .../ext/RadioButtonPickerFragment2.java | 208 +++++++++++++++ .../widget/RadioButtonPickerFragment.java | 16 +- 3 files changed, 462 insertions(+), 4 deletions(-) create mode 100644 src/com/android/settings/ext/AbstractListPreferenceController.java create mode 100644 src/com/android/settings/ext/RadioButtonPickerFragment2.java diff --git a/src/com/android/settings/ext/AbstractListPreferenceController.java b/src/com/android/settings/ext/AbstractListPreferenceController.java new file mode 100644 index 00000000000..a5686e370ee --- /dev/null +++ b/src/com/android/settings/ext/AbstractListPreferenceController.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2022 GrapheneOS + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.settings.ext; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.UserHandle; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.SparseIntArray; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.utils.CandidateInfoExtra; +import com.android.settingslib.widget.CandidateInfo; +import com.android.settingslib.widget.FooterPreference; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractListPreferenceController extends BasePreferenceController + implements DefaultLifecycleObserver { + + private Preference preference; + private Entries entries; + + @Nullable + public RadioButtonPickerFragment2 fragment; + + 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); + + public void getEntriesAsCandidates(ArrayList dst) { + Entries e = new Entries(mContext); + getEntries(e); + + dst.addAll(e.list); + } + + protected abstract int getCurrentValue(); + protected abstract boolean setValue(int val); + + @Override + public void updateState(Preference p) { + if (entries == null) { + entries = new Entries(mContext); + getEntries(entries); + } + + if (p != preference) { + p.setSingleLineTitle(false); + p.setPersistent(false); + this.preference = p; + } + + updatePreference(); + } + + void updatePreference() { + if (fragment != null) { + fragment.updateCandidates(); + } + + Preference p = preference; + if (p == null) { + return; + } + + int idx = entries.getIndexForValue(getCurrentValue()); + if (idx >= 0) { + p.setSummary(entries.list.get(idx).loadLabel()); + } else { + p.setSummary(null); + } + } + + public static class Entries { + private final Context context; + private final ArrayList list = 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(@StringRes int title, @StringRes int summary, int value) { + add(context.getText(title), context.getText(summary), 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) { + add(title, null, value, true); + } + + public void add(CharSequence title, CharSequence summary, int value) { + add(title, summary, value, true); + } + + public void add(CharSequence title, @Nullable CharSequence summary, int value, boolean enabled) { + String prefKey = Integer.toString(value); + list.add(new CandidateInfoExtra(title, summary, prefKey, enabled)); + valueToIndexMap.put(value, list.size() - 1); + } + + public int getIndexForValue(int val) { + return valueToIndexMap.get(val, -1); + } + } + + @Override + public boolean isSliceable() { + return false; + } + + @Override + public int getSliceHighlightMenuRes() { + return NO_RES; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return super.handlePreferenceTreeClick(preference); + } + + if (this.preference instanceof ListPreference) { + return super.handlePreferenceTreeClick(preference); + } + + UserHandle workProfileUser = getWorkProfileUser(); + boolean isForWork = workProfileUser != null; + + RadioButtonPickerFragment2.fillArgs(preference, this, isForWork); + + new SubSettingLauncher(preference.getContext()) + .setDestination(RadioButtonPickerFragment2.class.getName()) + .setSourceMetricsCategory(preference.getExtras().getInt(DashboardFragment.CATEGORY, + SettingsEnums.PAGE_UNKNOWN)) + .setTitleText(preference.getTitle()) + .setArguments(preference.getExtras()) + .setUserHandle(workProfileUser) + .launch(); + return true; + } + + public void addPrefsBeforeList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + + } + + public void addPrefsAfterList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + + } + + public FooterPreference addFooterPreference(PreferenceScreen screen, @StringRes int text) { + Context ctx = screen.getContext(); + return addFooterPreference(screen, ctx.getText(text), null, null); + } + + public FooterPreference addFooterPreference(PreferenceScreen screen, + @StringRes int text, String learnMoreUrl) { + return addFooterPreference(screen, text, R.string.learn_more, learnMoreUrl); + } + + public FooterPreference addFooterPreference( + PreferenceScreen screen, @StringRes int text, + @StringRes int learnMoreText, String learnMoreUrl) { + Context ctx = screen.getContext(); + Runnable learnMoreAction = () -> { + var intent = new Intent(Intent.ACTION_VIEW, Uri.parse(learnMoreUrl)); + ctx.startActivity(intent); + }; + return addFooterPreference(screen, ctx.getText(text), + ctx.getText(learnMoreText), learnMoreAction); + } + + public FooterPreference addFooterPreference( + PreferenceScreen screen, @StringRes int text, + @StringRes int learnMoreText, Runnable learnMoreAction) { + Context ctx = screen.getContext(); + return addFooterPreference(screen, ctx.getText(text), ctx.getText(learnMoreText), learnMoreAction); + } + + public FooterPreference addFooterPreference(PreferenceScreen screen, CharSequence text, + @Nullable CharSequence learnMoreText, + @Nullable Runnable learnMoreAction) { + var p = new FooterPreference(screen.getContext()); + p.setSelectable(false); + p.setSummary(text); + if (learnMoreText != null) { + p.setLearnMoreText(learnMoreText); + Objects.requireNonNull(learnMoreAction); + p.setLearnMoreAction(v -> learnMoreAction.run()); + } + p.setOrder(Preference.DEFAULT_ORDER); + screen.addPreference(p); + return p; + } + + protected final CharSequence getText(@StringRes int resId) { + return mContext.getText(resId); + } + + /** + * Returns whether to require user credential confirmation before changing this preference. + */ + protected boolean isCredentialConfirmationRequired() { + return false; + } +} diff --git a/src/com/android/settings/ext/RadioButtonPickerFragment2.java b/src/com/android/settings/ext/RadioButtonPickerFragment2.java new file mode 100644 index 00000000000..a44c9280af7 --- /dev/null +++ b/src/com/android/settings/ext/RadioButtonPickerFragment2.java @@ -0,0 +1,208 @@ +package com.android.settings.ext; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.utils.CandidateInfoExtra; +import com.android.settings.widget.RadioButtonPickerFragment; +import com.android.settingslib.widget.CandidateInfo; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import java.util.ArrayList; +import java.util.List; + +public class RadioButtonPickerFragment2 extends RadioButtonPickerFragment { + + private final ArrayList candidates = new ArrayList<>(); + + static final String KEY_PREF_CONTROLLER_CLASS = "pref_controller"; + static final String KEY_PREF_KEY = "pref_key"; + + private AbstractListPreferenceController prefController; + + public static void fillArgs(Preference pref, AbstractListPreferenceController pc, boolean isForWork) { + Bundle args = pref.getExtras(); + args.putString(KEY_PREF_CONTROLLER_CLASS, pc.getClass().getName()); + args.putString(KEY_PREF_KEY, pc.getPreferenceKey()); + args.putBoolean(EXTRA_FOR_WORK, isForWork); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + Bundle args = requireArguments(); + String prefControllerClass = args.getString(KEY_PREF_CONTROLLER_CLASS); + String prefKey = args.getString(KEY_PREF_KEY); + boolean forWork = args.getBoolean(EXTRA_FOR_WORK); + + Context ctx = requireContext(); + + prefController = (AbstractListPreferenceController) BasePreferenceController + .createInstance(ctx,prefControllerClass, prefKey, forWork); + prefController.fragment = this; + + super.onCreate(savedInstanceState); + } + + private static final String KEY_USER_CREDENTIAL_CONFIRMED = "user_credential_confirmed"; + private boolean userCredentialConfirmed; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(requireContext()); + setPreferenceScreen(ps); + + if (savedInstanceState != null) { + userCredentialConfirmed = savedInstanceState.getBoolean( + KEY_USER_CREDENTIAL_CONFIRMED, false); + } + + updateCandidates(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(KEY_USER_CREDENTIAL_CONFIRMED, userCredentialConfirmed); + } + + private Runnable onAfterCredentialConfirmed; + + private void runAfterCredentialConfirmation(Runnable runnable) { + if (userCredentialConfirmed) { + throw new IllegalStateException(); + } + + if (onAfterCredentialConfirmed != null) { + throw new IllegalStateException(); + } + + onAfterCredentialConfirmed = runnable; + + var b = new ChooseLockSettingsHelper.Builder(requireActivity()); + b.setActivityResultLauncher(credentialConfirmationLauncher); + b.setForegroundOnly(true); + b.show(); + } + + private final ActivityResultLauncher credentialConfirmationLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), + this::onCredentialConfirmationResult); + + private void onCredentialConfirmationResult(@Nullable ActivityResult result) { + Runnable r = onAfterCredentialConfirmed; + onAfterCredentialConfirmed = null; + + if (result != null && result.getResultCode() == Activity.RESULT_OK) { + userCredentialConfirmed = true; + if (r != null) { + r.run(); + } + } + } + + @Override + public void onStop() { + super.onStop(); + if (!requireActivity().isChangingConfigurations()) { + userCredentialConfirmed = false; + } + } + + @Override + protected int getPreferenceScreenResId() { + return -1; + } + + @Override + protected List getCandidates() { + candidates.clear(); + prefController.getEntriesAsCandidates(candidates); + return candidates; + } + + @Override + protected void addPrefsBeforeList(PreferenceScreen screen) { + prefController.addPrefsBeforeList(this, screen); + } + + @Override + protected void addPrefsAfterList(PreferenceScreen screen) { + prefController.addPrefsAfterList(this, screen); + } + + @Override + protected String getDefaultKey() { + return Integer.toString(prefController.getCurrentValue()); + } + + @Override + protected boolean setDefaultKey(String key) { + if (key.equals(getDefaultKey())) { + return true; + } + + if (!userCredentialConfirmed && prefController.isCredentialConfirmationRequired()) { + Context ctx = requireContext(); + LockPatternUtils lpu = FeatureFactory.getFeatureFactory() + .getSecurityFeatureProvider() + .getLockPatternUtils(ctx); + + if (lpu.isSecure(ctx.getUserId())) { + runAfterCredentialConfirmation(() -> prefController.setValue(Integer.parseInt(key))); + return false; + } + } + + return prefController.setValue(Integer.parseInt(key)); + } + + @Override + public void bindPreferenceExtra(SelectorWithWidgetPreference pref, String key, CandidateInfo info, + String defaultKey, String systemDefaultKey) { + pref.setSingleLineTitle(false); + + if (info instanceof CandidateInfoExtra) { + var cie = (CandidateInfoExtra) info; + pref.setSummary(cie.loadSummary()); + } + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + + @Override + public void onPause() { + super.onPause(); + prefController.onPause(this); + } + + private boolean updateCandidatesOnResume; + + @Override + public void onResume() { + super.onResume(); + prefController.onResume(this); + + if (updateCandidatesOnResume) { + updateCandidates(); + } else { + // updateCandidates() is called from onCreatePrefences() right before the first onResume() + updateCandidatesOnResume = true; + } + } +} diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java index 121458c714a..30d5c54f0dd 100644 --- a/src/com/android/settings/widget/RadioButtonPickerFragment.java +++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java @@ -55,7 +55,7 @@ public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragme SelectorWithWidgetPreference.OnClickListener { @VisibleForTesting - static final String EXTRA_FOR_WORK = "for_work"; + protected static final String EXTRA_FOR_WORK = "for_work"; private static final String TAG = "RadioButtonPckrFrgmt"; @VisibleForTesting boolean mAppendStaticPreferences = false; @@ -172,6 +172,12 @@ protected SelectorWithWidgetPreference createPreference() { return new SelectorWithWidgetPreference(getPrefContext()); } + protected void addPrefsBeforeList(PreferenceScreen screen) { + if (!mAppendStaticPreferences) { + addStaticPreferences(screen); + } + } + public void updateCandidates() { mCandidates.clear(); final List candidateList = getCandidates(); @@ -187,9 +193,7 @@ public void updateCandidates() { if (mIllustrationId != 0) { addIllustration(screen); } - if (!mAppendStaticPreferences) { - addStaticPreferences(screen); - } + addPrefsBeforeList(screen); final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId(); if (shouldShowItemNone()) { @@ -216,6 +220,10 @@ public void updateCandidates() { } } mayCheckOnlyRadioButton(); + addPrefsAfterList(screen); + } + + protected void addPrefsAfterList(PreferenceScreen screen) { if (mAppendStaticPreferences) { addStaticPreferences(screen); } From f277a57a1a52e308b6c3809477161e945318be96 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Tue, 14 Feb 2023 08:45:56 +0200 Subject: [PATCH 015/128] add base class for toggle preference controllers --- .../ext/AbstractTogglePrefController.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/com/android/settings/ext/AbstractTogglePrefController.java 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; + } +} From 837f2c9c52ddf6c9e36ae85c3b1f384029a5ffdf Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sun, 12 Feb 2023 19:44:07 +0200 Subject: [PATCH 016/128] add base classes for {Bool,Int}Setting preference controllers If no customization is needed, BoolSetting base class can be used directly from XML by adding settings:boolSettingField=" " to the preference XML definition. --- res/values/attrs.xml | 2 + .../core/PreferenceControllerListHelper.java | 3 + .../core/PreferenceXmlParserUtils.java | 14 ++- .../ext/BoolSettingPrefController.java | 90 +++++++++++++++++++ .../ext/ExtSettingControllerHelper.java | 72 +++++++++++++++ .../ext/ExtSettingPrefController.java | 12 +++ .../ext/IntSettingPrefController.java | 57 ++++++++++++ 7 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/ext/BoolSettingPrefController.java create mode 100644 src/com/android/settings/ext/ExtSettingControllerHelper.java create mode 100644 src/com/android/settings/ext/ExtSettingPrefController.java create mode 100644 src/com/android/settings/ext/IntSettingPrefController.java diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 200253acce6..d404f3ebfb0 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -68,6 +68,8 @@ + + diff --git a/src/com/android/settings/core/PreferenceControllerListHelper.java b/src/com/android/settings/core/PreferenceControllerListHelper.java index 9dc0a9fa108..30d41d88c3e 100644 --- a/src/com/android/settings/core/PreferenceControllerListHelper.java +++ b/src/com/android/settings/core/PreferenceControllerListHelper.java @@ -31,6 +31,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; +import com.android.settings.ext.BoolSettingPrefController; import com.android.settingslib.core.AbstractPreferenceController; import org.xmlpull.v1.XmlPullParserException; @@ -59,6 +60,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); @@ -68,6 +70,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 df3cd922752..478a79e74e6 100644 --- a/src/com/android/settings/core/PreferenceXmlParserUtils.java +++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java @@ -64,6 +64,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_FOR_WORK = 1 << 12; int FLAG_NEED_HIGHLIGHTABLE_MENU_KEY = 1 << 13; int FLAG_NEED_USER_RESTRICTION = 1 << 14; + + 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"; @@ -162,6 +166,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)); } @@ -230,6 +238,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); } @@ -269,4 +281,4 @@ private static String getUserRestriction(Context context, AttributeSet attrs) { preferenceAttributes.recycle(); return userRestriction; } -} \ No newline at end of file +} diff --git a/src/com/android/settings/ext/BoolSettingPrefController.java b/src/com/android/settings/ext/BoolSettingPrefController.java new file mode 100644 index 00000000000..00d69fdfd2c --- /dev/null +++ b/src/com/android/settings/ext/BoolSettingPrefController.java @@ -0,0 +1,90 @@ +/* + * 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.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; + +import com.android.settings.core.BasePreferenceController; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Objects; + +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 ExtSettingPrefController { + private final BoolSetting setting; + private final ExtSettingControllerHelper helper; + + protected BoolSettingPrefController(Context ctx, String key, BoolSetting setting) { + super(ctx, key); + helper = new ExtSettingControllerHelper(ctx, setting); + this.setting = setting; + } + + @Override + public int getAvailabilityStatus() { + return helper.getAvailabilityStatus(); + } + + @Override + public final boolean isChecked() { + return setting.get(mContext); + } + + @Override + public boolean setChecked(boolean isChecked) { + return setting.put(mContext, isChecked); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + helper.onResume(this); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + helper.onPause(this); + } + + // called by the setting observer + @Override + public void accept(BoolSetting boolSetting) { + if (preference != null) { + updateState(preference); + } + } + + // 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/ExtSettingControllerHelper.java b/src/com/android/settings/ext/ExtSettingControllerHelper.java new file mode 100644 index 00000000000..250ccb6e29e --- /dev/null +++ b/src/com/android/settings/ext/ExtSettingControllerHelper.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 GrapheneOS + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.settings.ext; + +import android.content.ContentResolver; +import android.content.Context; +import android.ext.settings.Setting; +import android.provider.Settings; + +import java.util.function.Consumer; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER; + +public class ExtSettingControllerHelper { + private final Context context; + private final T setting; + + ExtSettingControllerHelper(Context context, T setting) { + this.context = context; + this.setting = setting; + } + + public static int getGlobalSettingAvailability(Context ctx) { + return ctx.getUser().isSystem() ? AVAILABLE : DISABLED_FOR_USER; + } + + public static int getSecondaryUserOnlySettingAvailability(Context ctx) { + return ctx.getUser().isSystem() ? DISABLED_FOR_USER : AVAILABLE; + } + + public static int getDevModeSettingAvailability(Context ctx) { + ContentResolver cr = ctx.getContentResolver(); + String key = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED; + + return (Settings.Global.getInt(cr, key, 0) == 0) ? + CONDITIONALLY_UNAVAILABLE : AVAILABLE; + } + + int getAvailabilityStatus() { + if (setting.getScope() != Setting.Scope.PER_USER) { + return getGlobalSettingAvailability(context); + } + return AVAILABLE; + } + + private Object observer; + + void onResume(ExtSettingPrefController espc) { + registerObserver(espc); + } + + void onPause(ExtSettingPrefController espc) { + unregisterObserver(); + } + + void registerObserver(Consumer settingObserver) { + if (setting.canObserveState()) { + observer = setting.registerObserver(context, context.getMainThreadHandler(), settingObserver); + } + } + + void unregisterObserver() { + if (setting.canObserveState()) { + setting.unregisterObserver(context, observer); + } + } +} diff --git a/src/com/android/settings/ext/ExtSettingPrefController.java b/src/com/android/settings/ext/ExtSettingPrefController.java new file mode 100644 index 00000000000..f641e344b50 --- /dev/null +++ b/src/com/android/settings/ext/ExtSettingPrefController.java @@ -0,0 +1,12 @@ +package com.android.settings.ext; + +import android.ext.settings.Setting; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; + +import java.util.function.Consumer; + +interface ExtSettingPrefController extends DefaultLifecycleObserver, Consumer { +} diff --git a/src/com/android/settings/ext/IntSettingPrefController.java b/src/com/android/settings/ext/IntSettingPrefController.java new file mode 100644 index 00000000000..1660f54bf0b --- /dev/null +++ b/src/com/android/settings/ext/IntSettingPrefController.java @@ -0,0 +1,57 @@ +/* + * 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 androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; + +public abstract class IntSettingPrefController extends AbstractListPreferenceController + implements ExtSettingPrefController +{ + private final IntSetting setting; + + private final ExtSettingControllerHelper helper; + + protected IntSettingPrefController(Context ctx, String key, IntSetting setting) { + super(ctx, key); + this.setting = setting; + helper = new ExtSettingControllerHelper(ctx, setting); + } + + @Override + public int getAvailabilityStatus() { + return helper.getAvailabilityStatus(); + } + + @Override + protected final int getCurrentValue() { + return setting.get(mContext); + } + + @Override + protected boolean setValue(int val) { + return setting.put(mContext, val); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) { + helper.onResume(this); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + helper.onPause(this); + } + + // called by the setting observer + @Override + public void accept(IntSetting intSetting) { + updatePreference(); + } +} From 640892a054f13f4b5e093799463300094ae7fc25 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Tue, 30 Jul 2024 15:20:39 +0300 Subject: [PATCH 017/128] add Exploit protection screen --- AndroidManifest.xml | 17 +++++++++++ res/values/strings_ext.xml | 5 ++++ res/xml/exploit_protection_settings.xml | 16 ++++++++++ src/com/android/settings/Settings.java | 2 ++ .../core/gateway/SettingsGateway.java | 1 + .../dashboard/DashboardFragmentRegistry.java | 3 ++ .../ExploitProtectionFragment.java | 29 +++++++++++++++++++ 7 files changed, 73 insertions(+) create mode 100644 res/xml/exploit_protection_settings.xml create mode 100644 src/com/android/settings/safetycenter/ExploitProtectionFragment.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9cf39de9e6d..101e37fc778 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2211,6 +2211,23 @@ android:value="@string/menu_key_safety_center"/> + + + + + + + + + + + Enabled Disabled + Exploit protection + + App exploit protection + + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml new file mode 100644 index 00000000000..54da29f1394 --- /dev/null +++ b/res/xml/exploit_protection_settings.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index d5cf9aa2281..3af97f9c47a 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -522,4 +522,6 @@ public static class ResetMobileNetworkSettingsActivity extends SettingsActivity public static class HearingDevicesActivity extends SettingsActivity { /* empty */ } public static class HearingDevicesPairingActivity extends SettingsActivity { /* empty */ } public static class ContactsStorageSettingsActivity extends SettingsActivity { /* empty */ } + + public static class ExploitProtectionActivity extends SettingsActivity {} } diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 8541c7a6e49..4dee4dc0301 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -218,6 +218,7 @@ public class SettingsGateway { * security exception if the fragment it needs to display is not in this list. */ public static final String[] ENTRY_FRAGMENTS = { + com.android.settings.safetycenter.ExploitProtectionFragment.class.getName(), AdvancedConnectedDeviceDashboardFragment.class.getName(), CreateShortcut.class.getName(), BluetoothPairingDetail.class.getName(), diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java index 849a80b3592..02e42d4c6a2 100644 --- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java +++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java @@ -43,6 +43,7 @@ import com.android.settings.notification.SoundSettings; import com.android.settings.notification.zen.ZenModeSettings; import com.android.settings.privacy.PrivacyDashboardFragment; +import com.android.settings.safetycenter.ExploitProtectionFragment; import com.android.settings.safetycenter.MoreSecurityPrivacyFragment; import com.android.settings.security.LockscreenDashboardFragment; import com.android.settings.security.SecurityAdvancedSettings; @@ -71,6 +72,8 @@ public class DashboardFragmentRegistry { static { PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>(); + PARENT_TO_CATEGORY_KEY_MAP.put(ExploitProtectionFragment.class.getName(), + CategoryKey.CATEGORY_EXPLOIT_PROTECTION_SETTINGS); PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(), CategoryKey.CATEGORY_HOMEPAGE); PARENT_TO_CATEGORY_KEY_MAP.put( diff --git a/src/com/android/settings/safetycenter/ExploitProtectionFragment.java b/src/com/android/settings/safetycenter/ExploitProtectionFragment.java new file mode 100644 index 00000000000..2542012590b --- /dev/null +++ b/src/com/android/settings/safetycenter/ExploitProtectionFragment.java @@ -0,0 +1,29 @@ +package com.android.settings.safetycenter; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +@SearchIndexable +public class ExploitProtectionFragment extends DashboardFragment { + private static final String TAG = "ExploitProtFragment"; + + @Override + protected int getPreferenceScreenResId() { + return R.xml.exploit_protection_settings; + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + + @Override + protected String getLogTag() { + return TAG; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.exploit_protection_settings); +} From 0d8dcc4f1918845efeccbe2ce0b201a3b6cdf941 Mon Sep 17 00:00:00 2001 From: flawedworld <38294951+flawedworld@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:13:36 +0100 Subject: [PATCH 018/128] add device info field with bootloader version --- res/values/strings.xml | 2 ++ res/xml/firmware_version.xml | 9 ++++++ ...BootloaderVersionPreferenceController.java | 28 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/com/android/settings/deviceinfo/firmwareversion/BootloaderVersionPreferenceController.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 730c258601c..d87021f7901 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3227,6 +3227,8 @@ Baseband version + Bootloader version + Kernel version Build number diff --git a/res/xml/firmware_version.xml b/res/xml/firmware_version.xml index edc910b36cf..cf9779a6988 100644 --- a/res/xml/firmware_version.xml +++ b/res/xml/firmware_version.xml @@ -46,6 +46,15 @@ settings:enableCopying="true" settings:controller="com.android.settings.deviceinfo.firmwareversion.BasebandVersionPreferenceController"/> + + + Date: Mon, 19 Jul 2021 01:31:27 +0200 Subject: [PATCH 019/128] hide pattern screenlock option --- .../android/settings/password/ChooseLockGenericController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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. From 610c8a7f21ea6f35cec4943b3db9d92666014c5e Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Sun, 25 Jul 2021 14:44:48 -0400 Subject: [PATCH 020/128] drop support for showing nearby devices from Play --- res/values/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 4db12c329de..129ea31b004 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -518,7 +518,7 @@ false - content://com.google.android.gms.nearby.fastpair/device_status_list_item + From 2d7fe8e8c384b28f135e131cee8b19126f35a181 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 Oct 2021 02:17:56 +0000 Subject: [PATCH 021/128] Dark mode support for app installation restriction icon --- res/drawable/ic_settings_install.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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"> From c30f78cc3fddf1f2847ebbdac647905599e530ff Mon Sep 17 00:00:00 2001 From: Danny Lin Date: Wed, 7 Jul 2021 03:02:05 -0700 Subject: [PATCH 022/128] Add preference for increased touch sensitivity (glove mode) This preference controls the glove mode feature on Pixel devices for increased touch sensitivity without requiring a custom HAL or other device-side code. This is done by using the debug.touch_sensitivity_mode system property, which Settings has permission to change. The user-visible value is persisted in Settings.Secure, while the property is persisted in persist.vendor.touch_sensitivity_mode. Requires frameworks/base commit: "Add a config to state whether a device supports increased touch sensitivity." Requires device/google/* commit: "Express support for increased touch sensitivity." Closes: #1 Co-authored-by: Diab Neiroukh Change-Id: I86af721fde33226d314d8a44525f310828299a72 --- res/values/strings.xml | 4 ++ res/xml/display_settings.xml | 6 ++ .../TouchSensitivityPreferenceController.java | 61 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100755 src/com/android/settings/display/TouchSensitivityPreferenceController.java diff --git a/res/values/strings.xml b/res/values/strings.xml index d87021f7901..ba08bee91b5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2950,6 +2950,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 diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml index 3baf439035a..99e1cf9f9c7 100644 --- a/res/xml/display_settings.xml +++ b/res/xml/display_settings.xml @@ -166,6 +166,12 @@ android:summary="@string/peak_refresh_rate_summary" settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController"/> + + Date: Fri, 21 Jan 2022 22:05:44 +0200 Subject: [PATCH 023/128] add dynamic link to sandboxed Google Play settings --- res/values/strings.xml | 3 ++ res/xml/apps.xml | 13 +++++ .../applications/GmsCompatAppController.java | 48 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/com/android/settings/applications/GmsCompatAppController.java diff --git a/res/values/strings.xml b/res/values/strings.xml index ba08bee91b5..d79e6f282ee 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13916,4 +13916,7 @@ Circle to Search Touch and hold the Home button or the navigation handle to search using the content on your screen. + + Sandboxed Google Play + Sandboxed Google Play (work profile) diff --git a/res/xml/apps.xml b/res/xml/apps.xml index 77b210f1821..ec31f03eb63 100644 --- a/res/xml/apps.xml +++ b/res/xml/apps.xml @@ -134,4 +134,17 @@ + + + + diff --git a/src/com/android/settings/applications/GmsCompatAppController.java b/src/com/android/settings/applications/GmsCompatAppController.java new file mode 100644 index 00000000000..32fe7d05e60 --- /dev/null +++ b/src/com/android/settings/applications/GmsCompatAppController.java @@ -0,0 +1,48 @@ +package com.android.settings.applications; + +import android.app.compat.gms.GmsCompat; +import android.content.Context; +import android.content.Intent; +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 { + + public GmsCompatAppController(Context context, String key) { + super(context, key); + } + + @Override + public int getAvailabilityStatus() { + UserHandle workProfile = getWorkProfileUser(); + int userId = workProfile != null ? + workProfile.getIdentifier() : + UserHandle.myUserId(); + + return GmsCompat.isEnabledFor(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) { + mContext.startActivityAsUser(intent, workProfile); + } else { + mContext.startActivity(intent); + } + return true; + } +} From 34cc193c9b3a1a18191b6a42227b940a7bdf78d7 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Mon, 12 Aug 2024 17:45:43 +0300 Subject: [PATCH 024/128] add Private Space sandboxed Google Play link It's added to Private Space screen instead of the Apps screen (where regular and work profile links are) to avoid revealing whether the Private Space is present when the "Hide when locked" Private Space option is enabled. --- res/xml/private_space_settings.xml | 5 +++ .../applications/GmsCompatAppController.java | 41 +++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml index 53bbc5727d4..3f404d0db6c 100644 --- a/res/xml/private_space_settings.xml +++ b/res/xml/private_space_settings.xml @@ -56,6 +56,11 @@ settings:controller="com.android.settings.privatespace.HidePrivateSpaceSummaryController" settings:searchable="false" /> + + Date: Sat, 5 Mar 2022 17:37:47 +0000 Subject: [PATCH 025/128] Show Hardware SKU in About Phone --- res/values/strings_ext.xml | 1 + res/xml/hardware_info.xml | 9 ++++ .../HardwareSkuPreferenceController.java | 48 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/com/android/settings/deviceinfo/hardwareinfo/HardwareSkuPreferenceController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 3bda9789896..d80f4360aa0 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -1,5 +1,6 @@ + Hardware SKU Enable Enabled diff --git a/res/xml/hardware_info.xml b/res/xml/hardware_info.xml index e086a486ee0..8fef9371870 100644 --- a/res/xml/hardware_info.xml +++ b/res/xml/hardware_info.xml @@ -57,4 +57,13 @@ settings:controller="com.android.settings.deviceinfo.hardwareinfo.ManufacturedYearPreferenceController" settings:enableCopying="true"/> + + + 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"); + } +} From 537d0bacc55abcb2a6af065dfa7fa6d6b161370f Mon Sep 17 00:00:00 2001 From: Roman Royer Date: Wed, 4 May 2016 19:04:36 -0700 Subject: [PATCH 026/128] Allow sorting Applications by size Allow sorting applications by size in Settings->Apps just like in Settings->Storage/USB->Apps Change-Id: I9e022162110e1feff20f257992086e66735e0f5e --- .../applications/manageapplications/ManageApplications.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index b837e1e9c5d..143727ae1bf 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -863,9 +863,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 From 85d37f70b0f212bc6cd70c78781c9d061317c64a Mon Sep 17 00:00:00 2001 From: June Date: Wed, 16 Mar 2022 07:09:46 +0200 Subject: [PATCH 027/128] add a per-user setting for forwarding censored notifs Signed-off-by: June Co-authored-by: inthewaves Co-authored-by: June --- res/values/strings.xml | 3 + res/xml/user_settings.xml | 6 ++ ...ionsToCurrentUserPreferenceController.java | 60 +++++++++++++++++++ .../android/settings/users/UserSettings.java | 19 ++++++ 4 files changed, 88 insertions(+) create mode 100644 src/com/android/settings/users/SendCensoredNotificationsToCurrentUserPreferenceController.java diff --git a/res/values/strings.xml b/res/values/strings.xml index d79e6f282ee..f7e89ab1cfd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13917,6 +13917,9 @@ Touch and hold the Home button or the navigation handle to search using the content on your screen. + 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. + Sandboxed Google Play Sandboxed Google Play (work profile) diff --git a/res/xml/user_settings.xml b/res/xml/user_settings.xml index 59217226592..33a7adedb4f 100644 --- a/res/xml/user_settings.xml +++ b/res/xml/user_settings.xml @@ -100,4 +100,10 @@ android:fragment="com.android.settings.users.TimeoutToDockUserSettings" settings:controller="com.android.settings.users.TimeoutToDockUserPreferenceController"/> + + 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/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 28d2f4f33bd..0df53b5bc50 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -139,6 +139,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final String KEY_GUEST_USER_CATEGORY = "guest_user_category"; private static final String KEY_ALLOW_MULTIPLE_USERS = "allow_multiple_users"; private static final String KEY_USER_SETTINGS_SCREEN = "user_settings_screen"; + 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"; @@ -227,6 +229,7 @@ public class UserSettings extends SettingsPreferenceFragment private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; private GuestTelephonyPreferenceController mGuestTelephonyPreferenceController; private RemoveGuestOnExitPreferenceController mRemoveGuestOnExitPreferenceController; + private SendCensoredNotificationsToCurrentUserPreferenceController mSendCensoredNotificationsToCurrentUserPreferenceController; private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController; private TimeoutToDockUserPreferenceController mTimeoutToDockUserPreferenceController; private UserCreatingDialog mUserCreatingDialog; @@ -324,6 +327,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); @@ -334,11 +341,14 @@ public void onCreate(Bundle icicle) { mAddUserWhenLockedPreferenceController.displayPreference(screen); mGuestTelephonyPreferenceController.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(mGuestTelephonyPreferenceController.getPreferenceKey()) .setOnPreferenceChangeListener(mGuestTelephonyPreferenceController); @@ -420,6 +430,9 @@ public void onResume() { mRemoveGuestOnExitPreferenceController.updateState(screen.findPreference( mRemoveGuestOnExitPreferenceController.getPreferenceKey())); mSwitchBarController.updateState(); + mSendCensoredNotificationsToCurrentUserPreferenceController.updateState(screen.findPreference( + mSendCensoredNotificationsToCurrentUserPreferenceController.getPreferenceKey())); + if (mShouldUpdateUserList) { updateUI(); } @@ -1342,6 +1355,10 @@ void updateUserList() { mGuestTelephonyPreferenceController.getPreferenceKey()); mGuestTelephonyPreferenceController.updateState(guestCallPreference); + final Preference sendCensoredNotifs = getPreferenceScreen().findPreference( + mSendCensoredNotificationsToCurrentUserPreferenceController.getPreferenceKey()); + mSendCensoredNotificationsToCurrentUserPreferenceController.updateState(sendCensoredNotifs); + final Preference multiUserTopIntroPreference = getPreferenceScreen().findPreference( mMultiUserTopIntroPreferenceController.getPreferenceKey()); mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPreference); @@ -1868,6 +1885,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 */) From 4d4866de224f15de1af865df626ed86940304201 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 17 Aug 2022 12:40:26 -0400 Subject: [PATCH 028/128] fully enable location indicators by default --- .../location/LocationIndicatorsPreferenceController.java | 2 +- .../location/RecentLocationAccessPreferenceController.java | 6 +++--- .../location/RecentLocationAccessSeeAllFragment.java | 6 +++--- .../RecentLocationAccessSeeAllPreferenceController.java | 4 ++-- .../location/RecentLocationRequestPreferenceController.java | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) 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 c93b450ba01..7cfcbe55c9d 100644 --- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java +++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java @@ -53,7 +53,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; @VisibleForTesting @@ -108,9 +108,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 0ea9ffbdacb..b45bca51c4f 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 998e6f3f541..f5f0c5c6b00 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( From 60a6fffaf277380f2fcc2a21659db593efc8ee89 Mon Sep 17 00:00:00 2001 From: Pratyush Date: Fri, 19 Aug 2022 11:05:36 +0530 Subject: [PATCH 029/128] LTE Only Mode Co-authored-by: inthewaves Squashed with: Fix LTE Only mode on World Mode Co-authored-by: flawedworld --- res/values/strings.xml | 6 +++++ ...nabledNetworkModePreferenceController.java | 24 ++++++++++++++++++- ...ferredNetworkModePreferenceController.java | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index f7e89ab1cfd..d9214d9ac8e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12094,6 +12094,8 @@ Preferred network mode: CDMA/EvDo/GSM/WCDMA Preferred network mode: LTE + + Preferred network mode: LTE only Preferred network mode: GSM/WCDMA/LTE @@ -12159,8 +12161,12 @@ 4G LTE (recommended) + + LTE only 4G (recommended) + + 4G only 3G diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java index 8051711ddd4..120bd060cf8 100644 --- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java @@ -132,7 +132,7 @@ public EnabledNetworkModePreferenceController(Context context, String key) { @Override public int getAvailabilityStatus() { return getNetworkModePreferenceType(mContext, mSubId) - == NetworkModePreferenceType.EnabledNetworkMode + != NetworkModePreferenceType.PreferredNetworkMode ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @@ -362,6 +362,7 @@ public void updateConfig() { } void setPreferenceEntries() { + boolean lteOnlyUnsupported = false; mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); clearAllEntries(); @@ -377,6 +378,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) @@ -396,6 +398,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); @@ -413,6 +416,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) @@ -461,6 +465,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)) { @@ -644,6 +653,9 @@ void setPreferenceValueAndSummary(int networkMode) { break; } case TelephonyManager.NETWORK_MODE_LTE_ONLY: + setSummary(mShow4gForLTE + ? R.string.network_4G_only : R.string.network_lte_only); + break; case TelephonyManager.NETWORK_MODE_LTE_WCDMA: if (!mIsGlobalCdma) { setSelectedEntry(TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA); @@ -835,6 +847,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(TelephonyManager.NETWORK_MODE_LTE_ONLY); + } else { + mEntries.add(mContext.getString(R.string.network_lte_only)); + mEntriesValue.add(TelephonyManager.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 44091884f13..1a64a84b91d 100644 --- a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java @@ -216,7 +216,7 @@ private int getPreferredNetworkModeSummaryResId(int networkMode) { case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA: return R.string.preferred_network_mode_lte_tdscdma_summary; case TelephonyManager.NETWORK_MODE_LTE_ONLY: - return R.string.preferred_network_mode_lte_summary; + return R.string.preferred_network_mode_lte_only_summary; case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM: return R.string.preferred_network_mode_lte_tdscdma_gsm_summary; case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: From ae91b2aea3b4bb5f043dcc7e2e45705ee18d17bd Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sun, 13 Nov 2022 20:24:05 +0200 Subject: [PATCH 030/128] battery usage UI: use a fallback name for unknown components --- .../fuelgauge/batteryusage/BatteryEntry.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java index fef30563fe2..6b057484c9f 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java @@ -616,14 +616,10 @@ public static NameAndIcon getNameAndIconFromPowerComponent( iconId = R.drawable.ic_settings_aod; 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; } From 24fb7d50d59be65784be9cdcc8f1e587be7fab31 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Thu, 9 Jun 2022 11:27:32 +0200 Subject: [PATCH 031/128] Settings: Pass empty lottie resource for quickly open camera animation This lets us hide it properly. Change-Id: I02ac031a835236811b82a7de283335390ffebab9 --- res/drawable/quickly_open_camera.xml | 3 --- res/raw/lottie_quickly_open_camera.json | 0 res/xml/double_tap_power_settings.xml | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 res/drawable/quickly_open_camera.xml create mode 100644 res/raw/lottie_quickly_open_camera.json 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/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/xml/double_tap_power_settings.xml b/res/xml/double_tap_power_settings.xml index 783d045b5d5..a1e133d8e91 100644 --- a/res/xml/double_tap_power_settings.xml +++ b/res/xml/double_tap_power_settings.xml @@ -22,7 +22,7 @@ Date: Tue, 14 Feb 2023 10:05:57 +0200 Subject: [PATCH 032/128] add a toggle for auto-grants of OTHER_SENSORS permission --- res/values/strings_ext.xml | 4 ++++ res/xml/more_security_privacy_settings.xml | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index d80f4360aa0..60a38fd4cef 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -11,4 +11,8 @@ App exploit protection + 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. + diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml index 56706c3f91a..b22cc9e12ae 100644 --- a/res/xml/more_security_privacy_settings.xml +++ b/res/xml/more_security_privacy_settings.xml @@ -97,6 +97,13 @@ settings:controller= "com.android.settings.sound.MediaControlsLockScreenPreferenceController" /> + + Date: Tue, 14 Feb 2023 10:27:56 +0200 Subject: [PATCH 033/128] add a toggle for camera access from the lock screen --- res/values/strings_ext.xml | 2 ++ res/xml/screen_lock_settings.xml | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 60a38fd4cef..eb282e89d04 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -15,4 +15,6 @@ 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. + Allow camera access when locked + diff --git a/res/xml/screen_lock_settings.xml b/res/xml/screen_lock_settings.xml index 19061d9b855..d2553f61a25 100644 --- a/res/xml/screen_lock_settings.xml +++ b/res/xml/screen_lock_settings.xml @@ -52,4 +52,9 @@ android:key="power_button_instantly_locks" android:title="@string/lockpattern_settings_enable_power_button_instantly_locks" /> + + From 4138252ef4bf3f00ef5f38c2e47242bd5b52b657 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Tue, 14 Feb 2023 11:23:29 +0200 Subject: [PATCH 034/128] add auto-reboot setting Squashed with: 20d4d3e46ba4a626d6044df499b91c1fc5e5bd96 514f0ddf2adbb7b69b0750806020476cef8ed17e Co-authored-by: Daniel Micay --- res/values/strings_ext.xml | 3 ++ res/xml/exploit_protection_settings.xml | 5 ++ .../security/AutoRebootPrefController.java | 49 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/com/android/settings/security/AutoRebootPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index eb282e89d04..4cfb702a329 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -17,4 +17,7 @@ Allow camera access when locked + Auto reboot + Automatically reboot the device if it hasn\'t been unlocked within the selected duration of time. + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index 54da29f1394..faaf3b59180 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -5,6 +5,11 @@ android:key="exploit_protection_settings" android:title="@string/exploit_protection_settings"> + + diff --git a/src/com/android/settings/security/AutoRebootPrefController.java b/src/com/android/settings/security/AutoRebootPrefController.java new file mode 100644 index 00000000000..9c00e954e96 --- /dev/null +++ b/src/com/android/settings/security/AutoRebootPrefController.java @@ -0,0 +1,49 @@ +package com.android.settings.security; + +import android.content.Context; +import android.ext.settings.ExtSettings; + +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.ext.IntSettingPrefController; +import com.android.settings.ext.RadioButtonPickerFragment2; + +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 + public void addPrefsBeforeList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + addFooterPreference(screen, R.string.auto_reboot_footer, + "https://grapheneos.org/features#auto-reboot"); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.switch_off_text, 0); + entries.add(3, DAYS); + entries.add(2, DAYS); + entries.add(36, HOURS); + entries.add(1, DAYS); + entries.add(18, HOURS); + entries.add(12, HOURS); + entries.add(8, HOURS); + entries.add(4, HOURS); + entries.add(2, HOURS); + entries.add(1, HOURS); + entries.add(30, MINUTES); + entries.add(10, MINUTES); + } + + @Override + protected boolean isCredentialConfirmationRequired() { + return true; + } +} From cbbeaa6dc63c16bae1642acbb86594158491fcc5 Mon Sep 17 00:00:00 2001 From: r3g_5z Date: Sat, 18 Feb 2023 11:09:31 -0500 Subject: [PATCH 035/128] Add toggle for screenshot timestamp EXIF metadata Signed-off-by: r3g_5z --- res/values/strings_ext.xml | 3 +++ res/xml/more_security_privacy_settings.xml | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 4cfb702a329..813daad375c 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -20,4 +20,7 @@ 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 + diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml index b22cc9e12ae..c1b98843a45 100644 --- a/res/xml/more_security_privacy_settings.xml +++ b/res/xml/more_security_privacy_settings.xml @@ -104,6 +104,12 @@ android:summaryOff="@string/auto_grant_OTHER_SENSORS_permission_summary_off" settings:boolSettingField="android.ext.settings.ExtSettings AUTO_GRANT_OTHER_SENSORS_PERMISSION"/> + + Date: Fri, 3 Mar 2023 09:53:21 +0200 Subject: [PATCH 036/128] add GNSS SUPL setting --- res/values/strings_ext.xml | 8 ++++ res/xml/location_settings.xml | 5 +++ .../location/GnssSuplPrefController.java | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/com/android/settings/location/GnssSuplPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 813daad375c..2ef5fe0ff9d 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -23,4 +23,12 @@ 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. + + GrapheneOS proxy + Standard server + Off + Will make acquiring location lock significantly slower, especially if PSDS is turned off too + diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index 3f26d04287e..07c32921981 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -69,6 +69,11 @@ android:title="@string/location_services_preference_title" settings:controller="com.android.settings.location.LocationServicesPreferenceController"/> + + Date: Mon, 16 Oct 2023 09:42:40 -0400 Subject: [PATCH 037/128] disable auto confirm PIN toggle by default --- src/com/android/settings/password/ChooseLockPassword.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index eb7d66708b6..e4d53c94d4a 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -1022,9 +1022,6 @@ private void setAutoPinConfirmOption(boolean enabled, int length) { if (enabled && !mIsAlphaMode && isAutoPinConfirmPossible(length)) { mAutoPinConfirmOption.setVisibility(View.VISIBLE); mAutoConfirmSecurityMessage.setVisibility(View.VISIBLE); - if (!mIsAutoPinConfirmOptionSetManually) { - mAutoPinConfirmOption.setChecked(length == MIN_AUTO_PIN_REQUIREMENT_LENGTH); - } } else { mAutoPinConfirmOption.setVisibility(View.GONE); mAutoConfirmSecurityMessage.setVisibility(View.GONE); From 6d5a8a7a1fd53c4262ad487552fdc3effab317f5 Mon Sep 17 00:00:00 2001 From: r3g_5z Date: Wed, 15 Mar 2023 21:11:12 -0400 Subject: [PATCH 038/128] remove missing display resolution lottie animation AOSP does not provide these similar to the missing quickly open camera lottie animation Signed-off-by: r3g_5z --- src/com/android/settings/display/ScreenResolutionFragment.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/com/android/settings/display/ScreenResolutionFragment.java b/src/com/android/settings/display/ScreenResolutionFragment.java index d9718f66f93..ae37ab9e1b8 100644 --- a/src/com/android/settings/display/ScreenResolutionFragment.java +++ b/src/com/android/settings/display/ScreenResolutionFragment.java @@ -101,7 +101,6 @@ protected int getPreferenceScreenResId() { @Override protected void addStaticPreferences(PreferenceScreen screen) { - updateIllustrationImage(mImagePreference); screen.addPreference(mImagePreference); final FooterPreference footerPreference = new FooterPreference(screen.getContext()); @@ -223,7 +222,6 @@ protected boolean setDefaultKey(final String key) { } setDisplayMode(width); - updateIllustrationImage(mImagePreference); return true; } From 276cbdbf1017c430b294090dc0d16f26897f93be Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 24 Jan 2024 19:13:22 +0200 Subject: [PATCH 039/128] add toggle for eSIM support via Google's eSIM LPA package --- res/values/strings_ext.xml | 18 +++ res/xml/network_provider_internet.xml | 8 ++ .../network/GoogleEuiccLpaController.java | 108 ++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/com/android/settings/network/GoogleEuiccLpaController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 2ef5fe0ff9d..9c72938e5aa 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -31,4 +31,22 @@ Off Will make acquiring location lock significantly slower, especially if PSDS is turned off too + eSIM support + +A device restart is required to enable this setting. + + Warning + +"Turning off eSIM support will break the following functionality: + +• adding new eSIMs +• turning existing eSIMs on or off +• removing eSIMs, including from the lock screen (e.g. when eSIM is locked with a forgotten PIN) +• wiping eSIMs during factory reset + +Device will be automatically restarted." + + Restart + Proceed + diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml index 7a333672317..d1acf902104 100644 --- a/res/xml/network_provider_internet.xml +++ b/res/xml/network_provider_internet.xml @@ -56,6 +56,14 @@ settings:useAdminDisabledSummary="true" settings:controller="com.android.settings.network.MobileNetworkSummaryController" /> + + setEnabled(isChecked)); + b.show(); + + return false; + } +} From e54309ebc7785c5f718cd4fb91cc19fc803ffce1 Mon Sep 17 00:00:00 2001 From: r3g_5z Date: Sun, 2 Apr 2023 22:23:41 -0400 Subject: [PATCH 040/128] remote key provisioning server setting Co-authored-by: flawedworld Signed-off-by: r3g_5z --- res/values/strings_ext.xml | 5 +++ res/xml/network_provider_internet.xml | 6 ++++ .../RemoteProvisioningPrefController.java | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/com/android/settings/network/RemoteProvisioningPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 9c72938e5aa..0863ddb28cc 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -49,4 +49,9 @@ Device will be automatically restarted." Restart Proceed + Attestation key provisioning + GrapheneOS proxy + Google server + Off + diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml index d1acf902104..d04f6999b1c 100644 --- a/res/xml/network_provider_internet.xml +++ b/res/xml/network_provider_internet.xml @@ -126,4 +126,10 @@ android:summary="@string/cellular_security_summary" android:order="30" settings:controller="com.android.settings.network.CellularSecurityPreferenceController"/> + + diff --git a/src/com/android/settings/network/RemoteProvisioningPrefController.java b/src/com/android/settings/network/RemoteProvisioningPrefController.java new file mode 100644 index 00000000000..8617d6bd98c --- /dev/null +++ b/src/com/android/settings/network/RemoteProvisioningPrefController.java @@ -0,0 +1,36 @@ +/* + * 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.RemoteKeyProvisioningSettings; + +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, RemoteKeyProvisioningSettings.SERVER_SETTING); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.remote_provisioning_enabled_grapheneos_proxy, RemoteKeyProvisioningSettings.GRAPHENEOS_PROXY); + entries.add(R.string.remote_provisioning_enabled_google_server, RemoteKeyProvisioningSettings.STANDARD_SERVER); + } +} From d82b086afbc158e64125246c20280b00228cd238 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Thu, 6 Apr 2023 12:11:09 +0300 Subject: [PATCH 041/128] add toggle for special access to hardware accelerators by Google apps --- res/values/strings_ext.xml | 15 ++++++ res/xml/special_access.xml | 6 +++ ...oogleSpecialAcceleratorAccessFragment.java | 46 +++++++++++++++++++ ...pecialAcceleratorAccessPrefController.java | 34 ++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessFragment.java create mode 100644 src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 0863ddb28cc..e6f3d6a9044 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -54,4 +54,19 @@ Device will be automatically restarted." Google server Off + Special access to hardware accelerators for Google apps + Access granted + Access not granted + Grant special access to hardware accelerators to Google apps + +"Some Google apps expect to be able to access hardware accelerators in a special, more capable way than regular apps do. + +Examples of such apps: +• Pixel Camera +• Google Photos +• Google Recorder + +This setting applies to all users on the device." + + diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index f3a5f3216c7..32867ea63f7 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -231,4 +231,10 @@ android:name="classname" android:value="com.android.settings.Settings$ChangeNfcTagAppsActivity" /> + + diff --git a/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessFragment.java b/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessFragment.java new file mode 100644 index 00000000000..d681703f4cd --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessFragment.java @@ -0,0 +1,46 @@ +package com.android.settings.applications.specialaccess; + +import android.app.ActivityThread; +import android.ext.settings.BoolSetting; +import android.ext.settings.ExtSettings; +import android.os.RemoteException; + +import com.android.internal.util.GoogleCameraUtils; +import com.android.settings.R; +import com.android.settings.ext.BoolSettingFragment; +import com.android.settingslib.widget.FooterPreference; + +public class GoogleSpecialAcceleratorAccessFragment extends BoolSettingFragment { + private static final String TAG = GoogleSpecialAcceleratorAccessFragment.class.getSimpleName(); + + @Override + protected BoolSetting getSetting() { + return ExtSettings.ALLOW_GOOGLE_APPS_SPECIAL_ACCESS_TO_ACCELERATORS; + } + + @Override + protected CharSequence getTitle() { + return resText(R.string.Google_apps_special_accelerator_access_title); + } + + @Override + protected CharSequence getMainSwitchTitle() { + return resText(R.string.Google_apps_special_accelerator_access_main_switch); + } + + @Override + protected void onMainSwitchChanged(boolean state) { + if (GoogleCameraUtils.isCustomSeInfoNeededForAccessToAccelerators(requireContext())) { + try { + ActivityThread.getPackageManager().updateSeInfo(GoogleCameraUtils.PACKAGE_NAME); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + @Override + protected FooterPreference makeFooterPref(FooterPreference.Builder builder) { + return builder.setTitle(R.string.Google_apps_special_accelerator_access_footer).build(); + } +} diff --git a/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessPrefController.java b/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessPrefController.java new file mode 100644 index 00000000000..ac7d84fcbb0 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/GoogleSpecialAcceleratorAccessPrefController.java @@ -0,0 +1,34 @@ +package com.android.settings.applications.specialaccess; + +import android.content.Context; +import android.ext.settings.ExtSettings; + +import com.android.settings.R; +import com.android.settings.ext.BoolSettingFragmentPrefController; + +public class GoogleSpecialAcceleratorAccessPrefController extends BoolSettingFragmentPrefController { + + public GoogleSpecialAcceleratorAccessPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.ALLOW_GOOGLE_APPS_SPECIAL_ACCESS_TO_ACCELERATORS); + } + + @Override + public int getAvailabilityStatus() { + if (!mContext.getResources().getBoolean( + com.android.internal.R.bool.config_Google_apps_can_have_special_access_to_accelerators)) { + return UNSUPPORTED_ON_DEVICE; + } + + return super.getAvailabilityStatus(); + } + + @Override + protected CharSequence getSummaryOn() { + return resText(R.string.Google_apps_special_accelerator_access_summary_granted); + } + + @Override + protected CharSequence getSummaryOff() { + return resText(R.string.Google_apps_special_accelerator_access_summary_not_granted); + } +} From 4d784297fe161859730af60cd1a94e9756390540 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 3 May 2023 08:57:01 +0300 Subject: [PATCH 042/128] add GNSS PSDS setting --- res/values/strings_ext.xml | 11 ++++ res/xml/location_settings.xml | 5 ++ .../location/GnssPsdsPrefController.java | 58 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/com/android/settings/location/GnssPsdsPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index e6f3d6a9044..c4f04ee852e 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -69,4 +69,15 @@ Examples of such apps: This setting applies to all users on the device." + Predicted Satellite Data Service (PSDS) + +"If PSDS is turned on, static PSDS information files will be downloaded periodically to improve location resolution speed and accuracy. +No query or data is sent to the server. These files contain orbits and statuses of satellites, Earth environmental data and time adjustment information." + + GrapheneOS server + Qualcomm server + Standard server + Off + Will make acquiring location lock significantly slower, especially if SUPL is turned off too + diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index 07c32921981..4c99a9ae6a6 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -74,6 +74,11 @@ android:title="@string/pref_gnss_supl_title" settings:controller="com.android.settings.location.GnssSuplPrefController"/> + + Date: Wed, 24 May 2023 13:32:14 +0300 Subject: [PATCH 043/128] add connectivity checks setting --- res/values/strings_ext.xml | 10 +++++++ res/xml/network_provider_internet.xml | 6 ++++ .../ConnectivityChecksPrefController.java | 29 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 src/com/android/settings/network/ConnectivityChecksPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index c4f04ee852e..4b18be00da1 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -80,4 +80,14 @@ No query or data is sent to the server. These files contain orbits and statuses Off Will make acquiring location lock significantly slower, especially if SUPL is turned off too + GrapheneOS server + Standard (Google) server + Off + + Internet connectivity checks + +"Connectivity check is a special empty server request that is made to find out whether the current network has internet connection. +There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. + + diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml index d04f6999b1c..b4becf7c709 100644 --- a/res/xml/network_provider_internet.xml +++ b/res/xml/network_provider_internet.xml @@ -127,6 +127,12 @@ android:order="30" settings:controller="com.android.settings.network.CellularSecurityPreferenceController"/> + + Date: Wed, 24 May 2023 14:57:01 +0300 Subject: [PATCH 044/128] add common resources for auto-off settings --- res/values/strings_ext.xml | 1 + .../android/settings/ext/AutoOffSetting.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/com/android/settings/ext/AutoOffSetting.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 4b18be00da1..6a3212d86c6 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -1,6 +1,7 @@ Hardware SKU + Never Enable Enabled diff --git a/src/com/android/settings/ext/AutoOffSetting.java b/src/com/android/settings/ext/AutoOffSetting.java new file mode 100644 index 00000000000..98be94b8589 --- /dev/null +++ b/src/com/android/settings/ext/AutoOffSetting.java @@ -0,0 +1,25 @@ +package com.android.settings.ext; + +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 AutoOffSetting { + + public static void getEntries(AbstractListPreferenceController.Entries entries) { + entries.add(R.string.auto_off_never, 0); + entries.add(15, SECONDS); + entries.add(30, SECONDS); + entries.add(1, MINUTES); + entries.add(2, 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); + } +} From 4e69ac696fde422a177c9ff0e8fdf82a4c3df083 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Thu, 25 May 2023 16:25:33 +0300 Subject: [PATCH 045/128] add Wi-Fi auto-off setting --- res/values/strings_ext.xml | 2 + res/xml/exploit_protection_settings.xml | 5 +++ .../wifi/WifiAutoOffPrefController.java | 39 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/com/android/settings/wifi/WifiAutoOffPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 6a3212d86c6..b6d8f88afab 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -91,4 +91,6 @@ No query or data is sent to the server. These files contain orbits and statuses There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. + Turn off Wi-Fi automatically + If Wi-Fi is disconnected, it will be turned off after the selected timeout. diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index faaf3b59180..b5f3a0e1dde 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -10,6 +10,11 @@ android:title="@string/auto_reboot_title" settings:controller="com.android.settings.security.AutoRebootPrefController" /> + + diff --git a/src/com/android/settings/wifi/WifiAutoOffPrefController.java b/src/com/android/settings/wifi/WifiAutoOffPrefController.java new file mode 100644 index 00000000000..b8ec16bd69a --- /dev/null +++ b/src/com/android/settings/wifi/WifiAutoOffPrefController.java @@ -0,0 +1,39 @@ +package com.android.settings.wifi; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.ext.settings.ExtSettings; + +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.ext.AutoOffSetting; +import com.android.settings.ext.IntSettingPrefController; +import com.android.settings.ext.RadioButtonPickerFragment2; + +public class WifiAutoOffPrefController extends IntSettingPrefController { + + public WifiAutoOffPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.WIFI_AUTO_OFF); + } + + @Override + public int getAvailabilityStatus() { + int r = super.getAvailabilityStatus(); + if (r == AVAILABLE) { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) ? + AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + return r; + } + + @Override + public void addPrefsBeforeList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + addFooterPreference(screen, R.string.wifi_auto_off_footer); + } + + @Override + protected void getEntries(Entries entries) { + AutoOffSetting.getEntries(entries); + } +} From ca5a0e2e0cebe5079f3ece4b1ecfbaff787e17d2 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 24 May 2023 15:01:04 +0300 Subject: [PATCH 046/128] add Bluetooth auto-off setting --- res/values/strings_ext.xml | 6 +++ res/xml/exploit_protection_settings.xml | 5 +++ .../BluetoothAutoOffPrefController.java | 39 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/com/android/settings/bluetooth/BluetoothAutoOffPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index b6d8f88afab..5c41c20f89a 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -93,4 +93,10 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. Turn off Wi-Fi automatically If Wi-Fi is disconnected, it will be turned off after the selected timeout. + + Turn off Bluetooth automatically + +"If there are no connected devices, Bluetooth will turn off after the selected timeout." + + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index b5f3a0e1dde..bfd7e4dc3d9 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -15,6 +15,11 @@ android:title="@string/wifi_auto_off_title" settings:controller="com.android.settings.wifi.WifiAutoOffPrefController"/> + + diff --git a/src/com/android/settings/bluetooth/BluetoothAutoOffPrefController.java b/src/com/android/settings/bluetooth/BluetoothAutoOffPrefController.java new file mode 100644 index 00000000000..14791951802 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothAutoOffPrefController.java @@ -0,0 +1,39 @@ +package com.android.settings.bluetooth; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.ext.settings.ExtSettings; + +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.ext.AutoOffSetting; +import com.android.settings.ext.IntSettingPrefController; +import com.android.settings.ext.RadioButtonPickerFragment2; + +public class BluetoothAutoOffPrefController extends IntSettingPrefController { + + public BluetoothAutoOffPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.BLUETOOTH_AUTO_OFF); + } + + @Override + public int getAvailabilityStatus() { + int r = super.getAvailabilityStatus(); + if (r == AVAILABLE) { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) ? + AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + return r; + } + + @Override + public void addPrefsBeforeList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + addFooterPreference(screen, R.string.bluetooth_auto_off_footer); + } + + @Override + protected void getEntries(Entries entries) { + AutoOffSetting.getEntries(entries); + } +} From a06646bd0a6f9ed19267c61ef80872ccfa88962a Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Thu, 25 May 2023 16:47:42 +0300 Subject: [PATCH 047/128] add exec spawning setting --- res/values/strings_ext.xml | 4 +++ res/xml/exploit_protection_settings.xml | 6 +++- .../security/ExecSpawningFragment.java | 34 +++++++++++++++++++ .../security/ExecSpawningPrefController.java | 13 +++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/security/ExecSpawningFragment.java create mode 100644 src/com/android/settings/security/ExecSpawningPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 5c41c20f89a..c40e4a1fe1d 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -99,4 +99,8 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. "If there are no connected devices, Bluetooth will turn off after the selected timeout." + Secure app spawning + Use secure app spawning + Launch apps in a more secure way than Android which takes slightly longer and increases memory usage by app processes. + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index bfd7e4dc3d9..ada50481305 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -24,7 +24,11 @@ android:key="app_exploit_protection_settings" android:title="@string/app_exploit_protection_category"> - + diff --git a/src/com/android/settings/security/ExecSpawningFragment.java b/src/com/android/settings/security/ExecSpawningFragment.java new file mode 100644 index 00000000000..1ab1f744617 --- /dev/null +++ b/src/com/android/settings/security/ExecSpawningFragment.java @@ -0,0 +1,34 @@ +package com.android.settings.security; + +import android.ext.settings.BoolSetting; +import android.ext.settings.ExtSettings; +import android.net.Uri; + +import com.android.settings.R; +import com.android.settings.ext.BoolSettingFragment; +import com.android.settingslib.widget.FooterPreference; + +public class ExecSpawningFragment extends BoolSettingFragment { + + @Override + protected BoolSetting getSetting() { + return ExtSettings.EXEC_SPAWNING; + } + + @Override + protected CharSequence getTitle() { + return getText(R.string.exec_spawning_title); + } + + @Override + protected CharSequence getMainSwitchTitle() { + return getText(R.string.exec_spawning_title_inner); + } + + @Override + protected FooterPreference makeFooterPref(FooterPreference.Builder builder) { + FooterPreference p = builder.setTitle(R.string.exec_spawning_footer).build(); + setFooterPrefLearnMoreUri(p, Uri.parse("https://grapheneos.org/usage#exec-spawning")); + return p; + } +} diff --git a/src/com/android/settings/security/ExecSpawningPrefController.java b/src/com/android/settings/security/ExecSpawningPrefController.java new file mode 100644 index 00000000000..cd2be5cfc71 --- /dev/null +++ b/src/com/android/settings/security/ExecSpawningPrefController.java @@ -0,0 +1,13 @@ +package com.android.settings.security; + +import android.content.Context; +import android.ext.settings.ExtSettings; + +import com.android.settings.ext.BoolSettingFragmentPrefController; + +public class ExecSpawningPrefController extends BoolSettingFragmentPrefController { + + public ExecSpawningPrefController(Context ctx, String key) { + super(ctx, key, ExtSettings.EXEC_SPAWNING); + } +} From d17e49fc529dabd9d819f1dd76140d794d02ba7b Mon Sep 17 00:00:00 2001 From: flawedworld Date: Sun, 25 Jun 2023 13:53:58 +0100 Subject: [PATCH 048/128] Mark all DSU options as unsupported --- src/com/android/settings/development/DSULoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/development/DSULoader.java b/src/com/android/settings/development/DSULoader.java index fede67a7766..957e3d79e6f 100644 --- a/src/com/android/settings/development/DSULoader.java +++ b/src/com/android/settings/development/DSULoader.java @@ -305,7 +305,7 @@ Date getDeviceSPL() { } boolean isSupported() { - boolean supported = true; + boolean supported = false; String cpu = getDeviceCpu(); if (!mCpuAbi.equals(cpu)) { Slog.i(TAG, mCpuAbi + " != " + cpu); From 0a2013629453435b80f0bcaebbec81acb0dc44f6 Mon Sep 17 00:00:00 2001 From: quh4gko8 <88831734+quh4gko8@users.noreply.github.com> Date: Sat, 1 Jul 2023 07:07:57 +0000 Subject: [PATCH 049/128] Remove "Add users from lock screen" setting --- .../AddUserWhenLockedPreferenceController.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/com/android/settings/users/AddUserWhenLockedPreferenceController.java b/src/com/android/settings/users/AddUserWhenLockedPreferenceController.java index 2dc5b2d60d6..315be794607 100644 --- a/src/com/android/settings/users/AddUserWhenLockedPreferenceController.java +++ b/src/com/android/settings/users/AddUserWhenLockedPreferenceController.java @@ -57,19 +57,7 @@ public void updateState(Preference preference) { @Override public int getAvailabilityStatus() { - if (!mUserCaps.isAdmin()) { - return DISABLED_FOR_USER; - } else if (android.multiuser.Flags.newMultiuserSettingsUx()) { - if (mUserCaps.mDisallowAddUser && !mUserCaps.mDisallowAddUserSetByAdmin) { - return DISABLED_FOR_USER; - } else { - return AVAILABLE; - } - } else if (mUserCaps.disallowAddUser() || mUserCaps.disallowAddUserSetByAdmin()) { - return DISABLED_FOR_USER; - } else { - return mUserCaps.mUserSwitcherEnabled ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; - } + return UNSUPPORTED_ON_DEVICE; } @Override From ad29ffb9f45f00487ee5e594577fb93387858248 Mon Sep 17 00:00:00 2001 From: quh4gko8 <88831734+quh4gko8@users.noreply.github.com> Date: Sun, 23 Jul 2023 19:53:04 +0000 Subject: [PATCH 050/128] Factor out common code for reading/writing UserManager restrictions --- .../settings/users/UserDetailsSettings.java | 2 + .../settings/users/UserRestrictions.java | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/com/android/settings/users/UserRestrictions.java diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index ec4a4ff2558..aa7b95e735d 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -102,6 +102,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment @VisibleForTesting /** The user being studied (not the user doing the studying). */ UserInfo mUserInfo; + private UserRestrictions userRestrictions; @Override public int getMetricsCategory() { @@ -344,6 +345,7 @@ void initialize(Context context, Bundle arguments) { boolean isNewUser = arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false); mUserInfo = mUserManager.getUserInfo(userId); + userRestrictions = new UserRestrictions(mUserManager, mUserInfo); mSwitchUserPref = findPreference(KEY_SWITCH_USER); mPhonePref = findPreference(KEY_ENABLE_TELEPHONY); diff --git a/src/com/android/settings/users/UserRestrictions.java b/src/com/android/settings/users/UserRestrictions.java new file mode 100644 index 00000000000..7db3161c201 --- /dev/null +++ b/src/com/android/settings/users/UserRestrictions.java @@ -0,0 +1,38 @@ +package com.android.settings.users; + +import android.content.pm.UserInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; + +import java.util.List; + +final class UserRestrictions { + + final UserManager userManager; + final UserInfo userInfo; + + UserRestrictions(UserManager userManager, UserInfo userInfo) { + this.userManager = userManager; + this.userInfo = userInfo; + } + + boolean isSet(String restrictionKey) { + final boolean isSetFromUser = userManager.hasUserRestriction(restrictionKey, userInfo.getUserHandle()); + if (userInfo.isGuest()) { + return isSetFromUser || userManager.getDefaultGuestRestrictions().getBoolean(restrictionKey); + } + + return isSetFromUser; + } + + void set(String restrictionKey, boolean enableRestriction) { + if (userInfo.isGuest()) { + Bundle defaultGuestRestrictions = userManager.getDefaultGuestRestrictions(); + defaultGuestRestrictions.putBoolean(restrictionKey, enableRestriction); + userManager.setDefaultGuestRestrictions(defaultGuestRestrictions); + } else { + userManager.setUserRestriction(restrictionKey, enableRestriction, userInfo.getUserHandle()); + } + } +} From 20d1fb9aaf29c37fc5356b0819f4f29304a8d04e Mon Sep 17 00:00:00 2001 From: Pratyush Date: Sat, 3 Sep 2022 15:52:37 +0530 Subject: [PATCH 051/128] UserManager enable "install available apps" allowing adding (installed) apps from (owner) user manager ui. --- src/com/android/settings/users/UserDetailsSettings.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index aa7b95e735d..d4317bd614b 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -78,7 +78,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment private static final int DIALOG_CONFIRM_GRANT_ADMIN = 7; /** 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 static final int MESSAGE_PADDING = 20; private UserManager mUserManager; @@ -424,7 +424,6 @@ void initialize(Context context, Bundle arguments) { mPhonePref.setChecked(!mUserManager.hasUserRestriction( UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); mRemoveUserPref.setTitle(R.string.user_remove_user); - removePreference(KEY_APP_COPYING); } // Remove preference KEY_REMOVE_USER if DISALLOW_REMOVE_USER restriction is set From 0973c502fe5eedf5800bf4884d56951752563d78 Mon Sep 17 00:00:00 2001 From: quh4gko8 <88831734+quh4gko8@users.noreply.github.com> Date: Thu, 20 Jul 2023 05:07:49 +0000 Subject: [PATCH 052/128] UserManager settings for apps installs and updates --- res/values/strings_ext.xml | 11 ++ res/xml/user_details_settings.xml | 4 + .../users/UserAppsInstallSettings.java | 173 ++++++++++++++++++ .../settings/users/UserDetailsSettings.java | 11 ++ 4 files changed, 199 insertions(+) create mode 100644 src/com/android/settings/users/UserAppsInstallSettings.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index c40e4a1fe1d..6a77b6c95ef 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -103,4 +103,15 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. Use secure app spawning Launch apps in a more secure way than Android which takes slightly longer and increases memory usage by app processes. + + App installs and updates + + Enabled + Enabled for first party sources + Disabled + + Enable app installs and updates + Enable app installs and updates from first party sources only + Disable app installs and updates + diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml index 8e15d14bf5a..2cd01c1ef7d 100644 --- a/res/xml/user_details_settings.xml +++ b/res/xml/user_details_settings.xml @@ -33,6 +33,10 @@ android:key="app_and_content_access" android:icon="@drawable/ic_lock_closed" android:title="@string/user_restrictions_title" /> + getCandidates() { + Context ctx = requireContext(); + ArrayList candidates = new ArrayList<>(3); + if (!userRestrictions.userInfo.isGuest()) { + candidates.add(new CandidateInfoExtra(ctx.getString(R.string.user_app_install_enabled), + ctx.getString(R.string.user_app_install_enabled_desc), + INSTALL_ENABLED, true)); + } + candidates.add(new CandidateInfoExtra(ctx.getString(R.string.user_app_install_enabled_first_party_sources), + ctx.getString(R.string.user_app_install_enabled_first_party_sources_desc), + INSTALL_FIRST_PARTY_ENABLED, true)); + candidates.add(new CandidateInfoExtra(ctx.getString(R.string.user_app_install_disabled), + ctx.getString(R.string.user_app_install_disabled_desc), + INSTALL_DISABLED, true)); + return candidates; + } + + @Override + public void bindPreferenceExtra(SelectorWithWidgetPreference pref, String key, CandidateInfo info, + String defaultKey, String systemDefaultKey) { + pref.setSingleLineTitle(false); + + if (info instanceof CandidateInfoExtra) { + var cie = (CandidateInfoExtra) info; + pref.setSummary(cie.loadSummary()); + } + } + + @Override + protected String getDefaultKey() { + return getCurrentInstallRestriction(userRestrictions); + } + + @Override + protected boolean setDefaultKey(String key) { + if (key == null) { + return false; + } + + switch (key) { + case INSTALL_ENABLED: + if (userRestrictions.userInfo.isGuest()) { + return false; + } + userRestrictions.set(UserManager.DISALLOW_INSTALL_APPS, false); + userRestrictions.set(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, false); + return true; + case INSTALL_FIRST_PARTY_ENABLED: + userRestrictions.set(UserManager.DISALLOW_INSTALL_APPS, false); + if (!userRestrictions.userInfo.isGuest()) { + userRestrictions.set(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true); + } + return true; + case INSTALL_DISABLED: + userRestrictions.set(UserManager.DISALLOW_INSTALL_APPS, true); + if (!userRestrictions.userInfo.isGuest()) { + userRestrictions.set(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true); + } + return true; + default: + return false; + } + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + + @Override + public void onResume() { + super.onResume(); + updateCandidates(); + } +} diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index d4317bd614b..52b8e6f7aa5 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -66,6 +66,8 @@ public class UserDetailsSettings extends SettingsPreferenceFragment 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_APP_INSTALLS = "app_installs"; + /** Integer extra containing the userId to manage */ static final String EXTRA_USER_ID = "user_id"; @@ -98,6 +100,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment Preference mRemoveUserPref; @VisibleForTesting TwoStatePreference mGrantAdminPref; + Preference mAppsInstallsPref; @VisibleForTesting /** The user being studied (not the user doing the studying). */ @@ -135,6 +138,8 @@ public void onResume() { if (mUserInfo.isGuest() && mGuestUserAutoCreated) { mRemoveUserPref.setEnabled((mUserInfo.flags & UserInfo.FLAG_INITIALIZED) != 0); } + mAppsInstallsPref.setSummary(UserAppsInstallSettings.getDescription( + requireContext(), userRestrictions)); } @Override @@ -175,6 +180,9 @@ public boolean onPreferenceClick(Preference preference) { } else if (preference == mAppCopyingPref) { openAppCopyingScreen(); return true; + } else if (preference == mAppsInstallsPref) { + UserAppsInstallSettings.launch(preference, mUserInfo.id); + return true; } return false; } @@ -355,6 +363,7 @@ void initialize(Context context, Bundle arguments) { mGrantAdminPref = findPreference(KEY_GRANT_ADMIN); mGrantAdminPref.setChecked(mUserInfo.isAdmin()); + mAppsInstallsPref = findPreference(KEY_APP_INSTALLS); mSwitchUserPref.setVisible(mUserCaps.mUserSwitchingUiEnabled); @@ -391,6 +400,7 @@ void initialize(Context context, Bundle arguments) { removePreference(KEY_REMOVE_USER); removePreference(KEY_APP_AND_CONTENT_ACCESS); removePreference(KEY_APP_COPYING); + removePreference(KEY_APP_INSTALLS); } else { if (!Utils.isVoiceCapable(context)) { // no telephony removePreference(KEY_ENABLE_TELEPHONY); @@ -435,6 +445,7 @@ void initialize(Context context, Bundle arguments) { } mRemoveUserPref.setOnPreferenceClickListener(this); + mAppsInstallsPref.setOnPreferenceClickListener(this); mPhonePref.setOnPreferenceChangeListener(this); mGrantAdminPref.setOnPreferenceChangeListener(this); mAppAndContentAccessPref.setOnPreferenceClickListener(this); From cf55b8cd766a9f17e4f189da5028f8717a04e229 Mon Sep 17 00:00:00 2001 From: quh4gko8 <88831734+quh4gko8@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:19:41 +0000 Subject: [PATCH 053/128] UserManager settings for running in background --- res/values/strings_ext.xml | 2 ++ res/xml/user_details_settings.xml | 4 ++++ .../android/settings/users/UserDetailsSettings.java | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 6a77b6c95ef..bb407783a11 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -113,5 +113,7 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. Enable app installs and updates Enable app installs and updates from first party sources only Disable app installs and updates + + Allow running in background diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml index 2cd01c1ef7d..5f6df097ec9 100644 --- a/res/xml/user_details_settings.xml +++ b/res/xml/user_details_settings.xml @@ -25,6 +25,10 @@ android:key="user_grant_admin" android:icon="@drawable/ic_admin_panel_settings" android:title="@string/user_grant_admin" /> + Date: Wed, 26 Jul 2023 09:45:29 +0000 Subject: [PATCH 054/128] Disable app copying preference when the user has DISALLOW_INSTALL_APPS restriction --- src/com/android/settings/users/UserDetailsSettings.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index 9de016ba353..d3d4692da50 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -143,6 +143,7 @@ public void onResume() { } mAppsInstallsPref.setSummary(UserAppsInstallSettings.getDescription( requireContext(), userRestrictions)); + mAppCopyingPref.setEnabled(!userRestrictions.isSet(UserManager.DISALLOW_INSTALL_APPS)); } @Override From 550a0e7c935ee2fdf185959e4a7b1b22212ebceb Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 11:24:34 +0200 Subject: [PATCH 055/128] add support for disabling non-system apps --- .../settings/spa/app/appinfo/AppDisableButton.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/spa/app/appinfo/AppDisableButton.kt b/src/com/android/settings/spa/app/appinfo/AppDisableButton.kt index 555e9f14c61..07d583cde30 100644 --- a/src/com/android/settings/spa/app/appinfo/AppDisableButton.kt +++ b/src/com/android/settings/spa/app/appinfo/AppDisableButton.kt @@ -31,6 +31,7 @@ import com.android.settingslib.spa.widget.dialog.AlertDialogButton import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.framework.common.userManager +import com.android.settingslib.spaprivileged.model.app.installed import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed import com.android.settingslib.Utils as SettingsLibUtils @@ -47,7 +48,7 @@ class AppDisableButton( @Composable fun getActionButton(app: ApplicationInfo): ActionButton? { - if (!app.isSystemApp) return null + if (!app.installed) return null return when { app.enabled && !app.isDisabledUntilUsed -> { @@ -62,6 +63,8 @@ class AppDisableButton( * Gets whether a package can be disabled. */ private fun ApplicationInfo.canBeDisabled(): Boolean = when { + !isSystemApp -> appButtonRepository.isAllowUninstallOrArchive(context, this) + // Try to prevent the user from bricking their phone by not allowing disabling of apps // signed with the system certificate. isSignedWithPlatformKey -> false @@ -98,7 +101,12 @@ class AppDisableButton( // Currently we apply the same device policy for both the uninstallation and disable // button. if (!appButtonRepository.isUninstallBlockedByAdmin(app)) { - dialogPresenter.open() + if (app.isSystemApp) { + dialogPresenter.open() + } else { + packageInfoPresenter.disable() + } + } } } From 06fed86ae3676ccb8de7709bccb1420842ddb2cd Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 12:29:45 +0200 Subject: [PATCH 056/128] don't show "More options" button in App info uninstall dialog That button opens app's App info screen. --- src/com/android/settings/spa/app/AppUtil.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/spa/app/AppUtil.kt b/src/com/android/settings/spa/app/AppUtil.kt index 64da61380f5..7b319aeddeb 100644 --- a/src/com/android/settings/spa/app/AppUtil.kt +++ b/src/com/android/settings/spa/app/AppUtil.kt @@ -37,6 +37,7 @@ fun Context.startUninstallActivity( val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers) + putExtra(Intent.EXTRA_UNINSTALL_SHOW_MORE_OPTIONS_BUTTON, false) } startActivityAsUser(intent, userHandle) -} \ No newline at end of file +} From d7268a89c331d9d4768e9fc0163882a2678f2763 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 11:19:46 +0200 Subject: [PATCH 057/128] add Storage Scopes link to the "App info" screen --- res/values/strings.xml | 2 ++ .../spa/app/appinfo/AppInfoSettings.kt | 3 +- .../app/appinfo/AppStorageScopesPreference.kt | 32 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/spa/app/appinfo/AppStorageScopesPreference.kt diff --git a/res/values/strings.xml b/res/values/strings.xml index d9214d9ac8e..e69ffbbb761 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13928,4 +13928,6 @@ Sandboxed Google Play Sandboxed Google Play (work profile) + + Storage Scopes diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 78fb8df123b..46b29d7915a 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -147,6 +147,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { AppAllServicesPreference(app) AppNotificationPreference(app) AppPermissionPreference(app) + AppStorageScopesPreference(app) AppStoragePreference(app) InstantAppDomainsPreference(app) AppDataUsagePreference(app) @@ -181,4 +182,4 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { } fun isArchivingEnabled(featureFlags: PmFeatureFlags) = - featureFlags.archiving() || Flags.appArchiving() \ No newline at end of file + featureFlags.archiving() || Flags.appArchiving() diff --git a/src/com/android/settings/spa/app/appinfo/AppStorageScopesPreference.kt b/src/com/android/settings/spa/app/appinfo/AppStorageScopesPreference.kt new file mode 100644 index 00000000000..cbcd8944f56 --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AppStorageScopesPreference.kt @@ -0,0 +1,32 @@ +package com.android.settings.spa.app.appinfo + +import android.app.StorageScope +import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.content.pm.GosPackageStateFlag +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spaprivileged.model.app.userHandle +import com.android.settingslib.spaprivileged.model.app.userId + +@Composable +fun AppStorageScopesPreference(app: ApplicationInfo) { + val pkgName = app.packageName + if (!GosPackageState.get(pkgName, app.userId).hasFlag(GosPackageStateFlag.STORAGE_SCOPES_ENABLED)) { + return + } + + val context = LocalContext.current + + Preference(object : PreferenceModel { + override val title = stringResource(R.string.storage_scopes) + override val onClick = { + val intent = StorageScope.createConfigActivityIntent(pkgName) + context.startActivityAsUser(intent, app.userHandle) + } + }) +} From fd4436a4b98e2e30ac2e476d4b481d13549cd851 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 11:20:31 +0200 Subject: [PATCH 058/128] add Storage Scopes link to "All files access" screen --- .../spa/app/specialaccess/AllFilesAccess.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/com/android/settings/spa/app/specialaccess/AllFilesAccess.kt b/src/com/android/settings/spa/app/specialaccess/AllFilesAccess.kt index f6c08c0ca7c..6dba4817a2a 100644 --- a/src/com/android/settings/spa/app/specialaccess/AllFilesAccess.kt +++ b/src/com/android/settings/spa/app/specialaccess/AllFilesAccess.kt @@ -18,11 +18,19 @@ package com.android.settings.spa.app.specialaccess import android.Manifest import android.app.AppOpsManager +import android.app.StorageScope import android.app.settings.SettingsEnums import android.content.Context +import android.content.pm.PackageInfo +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.overlay.FeatureFactory.Companion.featureFactory +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spaprivileged.model.app.AppOps +import com.android.settingslib.spaprivileged.model.app.userHandle import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider @@ -54,4 +62,18 @@ class AllFilesAccessListModel(context: Context) : AppOpPermissionListModel(conte } featureFactory.metricsFeatureProvider.action(context, category, "") } + + @Composable + override fun extContent(record: AppOpPermissionRecord, pkgInfo: PackageInfo) { + val context = LocalContext.current + + Preference(object : PreferenceModel { + override val title = stringResource(R.string.storage_scopes) + override val onClick = { + val app = record.app + val i = StorageScope.createConfigActivityIntent(app.packageName) + context.startActivityAsUser(i, app.userHandle) + } + }) + } } From 135f44bec8d5b672d7188fe7d3eca7cb74cbf805 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Fri, 8 Jul 2022 13:40:22 +0300 Subject: [PATCH 059/128] add OBB access toggle to "Install unknown apps" screen --- res/values/strings.xml | 3 ++ .../app/specialaccess/InstallUnknownApps.kt | 52 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index e69ffbbb761..c284ff835e6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13930,4 +13930,7 @@ Sandboxed Google Play (work profile) Storage Scopes + + Allow access to Android/obb folder + Required for installation of some large apps (mostly games) that use OBB expansion files diff --git a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt index d6fe7160dbf..0a3d5219bc9 100644 --- a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt +++ b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt @@ -22,10 +22,18 @@ import android.app.AppOpsManager import android.app.AppOpsManager.MODE_DEFAULT import android.content.Context import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.content.pm.GosPackageStateFlag +import android.content.pm.PackageInfo import android.os.UserManager import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settingslib.spa.lifecycle.collectAsCallbackWithLifecycle +import com.android.settingslib.spa.widget.preference.SwitchPreference +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spaprivileged.model.app.AppOps import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppRecord @@ -44,7 +52,26 @@ object InstallUnknownAppsListProvider : TogglePermissionAppListProvider { data class InstallUnknownAppsRecord( override val app: ApplicationInfo, val appOpsController: AppOpsController, -) : AppRecord +) : AppRecord { + val isObbFlagSet = mutableStateOf(isObbFlagSet()) + + fun isObbFlagSet(): Boolean { + return GosPackageState.get(app.packageName, app.userId).hasFlag(GosPackageStateFlag.ALLOW_ACCESS_TO_OBB_DIRECTORY) + } + + fun setObbFlagState(state: Boolean): Boolean { + GosPackageState.edit(app.packageName, app.userId).run { + setFlagState(GosPackageStateFlag.ALLOW_ACCESS_TO_OBB_DIRECTORY, state) + killUidAfterApply() + if (apply()) { + isObbFlagSet.value = state + return true + } else { + return false + } + } + } +} class InstallUnknownAppsListModel(private val context: Context) : TogglePermissionAppListModel { @@ -84,6 +111,29 @@ class InstallUnknownAppsListModel(private val context: Context) : override fun setAllowed(record: InstallUnknownAppsRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) + if (!newAllowed) { + record.setObbFlagState(false) + } + } + + @Composable + override fun extContent(record: InstallUnknownAppsRecord, pkgInfo: PackageInfo) { + val context = LocalContext.current + + SwitchPreference(object : SwitchPreferenceModel { + override val title = stringResource(R.string.allow_access_to_obb_directory_title) + override val summary = { + context.getString(R.string.allow_access_to_obb_directory_summary) + } + + override val checked = { + record.isObbFlagSet.value + } + override val onCheckedChange = { newChecked: Boolean -> + record.setObbFlagState(newChecked) + Unit + } + }) } companion object { From 724455e9b386bda4205a85733d616a993dd157ae Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 11:22:22 +0200 Subject: [PATCH 060/128] add Contact Scopes link to the "App info" screen --- res/values/strings_ext.xml | 2 ++ .../app/appinfo/AppContactScopesPreference.kt | 32 +++++++++++++++++++ .../spa/app/appinfo/AppInfoSettings.kt | 1 + 3 files changed, 35 insertions(+) create mode 100644 src/com/android/settings/spa/app/appinfo/AppContactScopesPreference.kt diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index bb407783a11..b3b42b14bfb 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -81,6 +81,8 @@ No query or data is sent to the server. These files contain orbits and statuses Off Will make acquiring location lock significantly slower, especially if SUPL is turned off too + Contact Scopes + GrapheneOS server Standard (Google) server Off diff --git a/src/com/android/settings/spa/app/appinfo/AppContactScopesPreference.kt b/src/com/android/settings/spa/app/appinfo/AppContactScopesPreference.kt new file mode 100644 index 00000000000..be4cf6fadbc --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AppContactScopesPreference.kt @@ -0,0 +1,32 @@ +package com.android.settings.spa.app.appinfo + +import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.content.pm.GosPackageStateFlag +import android.ext.cscopes.ContactScopesApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spaprivileged.model.app.userHandle +import com.android.settingslib.spaprivileged.model.app.userId + +@Composable +fun AppContactScopesPreference(app: ApplicationInfo) { + val pkgName = app.packageName + if (!GosPackageState.get(pkgName, app.userId).hasFlag(GosPackageStateFlag.CONTACT_SCOPES_ENABLED)) { + return + } + + val context = LocalContext.current + + Preference(object : PreferenceModel { + override val title = stringResource(R.string.contact_scopes) + override val onClick = { + val intent = ContactScopesApi.createConfigActivityIntent(pkgName) + context.startActivityAsUser(intent, app.userHandle) + } + }) +} diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 46b29d7915a..cfe8f368bf5 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -148,6 +148,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { AppNotificationPreference(app) AppPermissionPreference(app) AppStorageScopesPreference(app) + AppContactScopesPreference(app) AppStoragePreference(app) InstantAppDomainsPreference(app) AppDataUsagePreference(app) From efce39537edbe8d3cfacabfb3e85cbd7d8b147ac Mon Sep 17 00:00:00 2001 From: empratyush Date: Mon, 20 Jun 2022 22:16:29 +0300 Subject: [PATCH 061/128] Reverse Wireless Charging UI --- Android.bp | 1 + res/values/strings.xml | 3 + res/xml/power_usage_summary.xml | 6 ++ .../BatterySharePreferenceController.java | 99 +++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 src/com/android/settings/display/BatterySharePreferenceController.java diff --git a/Android.bp b/Android.bp index 907ff12bc86..420bf5ab07c 100644 --- a/Android.bp +++ b/Android.bp @@ -93,6 +93,7 @@ android_library { // Settings dependencies "FingerprintManagerInteractor", "MediaDrmSettingsFlagsLib", + "ReverseWirelessCharging", "Settings-change-ids", "SettingsLib", "SettingsLibActivityEmbedding", diff --git a/res/values/strings.xml b/res/values/strings.xml index c284ff835e6..3b84d3e2657 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13933,4 +13933,7 @@ Allow access to Android/obb folder Required for installation of some large apps (mostly games) that use OBB expansion files + + Charge other devices by placing them on the back of your phone + Battery share diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 21a836d6d9a..c86ff0ac78f 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -70,6 +70,12 @@ android:summary="@string/battery_percentage_description" settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" /> + + 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); + ((TwoStatePreference) 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); + } +} From 597b421b9cafda2bf7e70916684e68b699f96f02 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Fri, 15 Sep 2023 10:03:16 +0300 Subject: [PATCH 062/128] hide the "Storage manager" toggle from Storage screen "Storage manager" is supposed to automatically remove backed up photos and videos. This functionality is not implemented in AOSP, i.e. this toggle doesn't actually work. --- res/values/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index 129ea31b004..9f8460ba664 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -612,7 +612,7 @@ content://com.android.providers.media.documents/root/others_root - true + false Allow running in background + "Warning: enabling this setting might cause some apps to stop working. + diff --git a/src/com/android/settings/ext/AppPrefUtils.java b/src/com/android/settings/ext/AppPrefUtils.java new file mode 100644 index 00000000000..3ae426bf36f --- /dev/null +++ b/src/com/android/settings/ext/AppPrefUtils.java @@ -0,0 +1,14 @@ +package com.android.settings.ext; + +import android.content.Context; + +import com.android.settings.R; + +import androidx.annotation.StringRes; + +public class AppPrefUtils { + + public static String getFooterForDefaultHardeningSetting(Context ctx, @StringRes int baseText) { + return ctx.getString(R.string.app_exploit_protection_default_value_warning) + "\n\n" + ctx.getString(baseText); + } +} From e2a85c7ff52a884e5f22d0bf8a32a858c46cf8d2 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 15 Nov 2023 13:44:08 +0200 Subject: [PATCH 065/128] add base class for App info single-choice fragments --- .../appinfo/RadioButtonAppInfoFragment.java | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/com/android/settings/applications/appinfo/RadioButtonAppInfoFragment.java diff --git a/src/com/android/settings/applications/appinfo/RadioButtonAppInfoFragment.java b/src/com/android/settings/applications/appinfo/RadioButtonAppInfoFragment.java new file mode 100644 index 00000000000..26fa78beb4d --- /dev/null +++ b/src/com/android/settings/applications/appinfo/RadioButtonAppInfoFragment.java @@ -0,0 +1,228 @@ +package com.android.settings.applications.appinfo; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.net.Uri; +import android.os.Bundle; +import android.util.TypedValue; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreferenceCompat; + +import com.android.settings.applications.AppInfoWithHeader; +import com.android.settings.applications.SpacePreference; +import com.android.settingslib.widget.FooterPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import java.util.Collections; +import java.util.List; + +public abstract class RadioButtonAppInfoFragment extends AppInfoWithHeader implements SelectorWithWidgetPreference.OnClickListener { + + public static class Entry { + public final int id; + public CharSequence title; + @Nullable + public CharSequence summary; + public boolean isChecked; + public boolean isEnabled; + + public Entry(int id, CharSequence title, @Nullable CharSequence summary, boolean isChecked, boolean isEnabled) { + this.id = id; + this.title = title; + this.summary = summary; + this.isChecked = isChecked; + this.isEnabled = isEnabled; + } + } + + public static Entry createEntry(int id, CharSequence title) { + return new Entry(id, title, null, false, true); + } + + public Entry createEntry(int id, @StringRes int title) { + return createEntry(id, getText(title)); + } + + public Entry createEntry(@StringRes int title) { + return createEntry(title, title); + } + + public abstract Entry[] getEntries(); + + public boolean hasFooter() { + return true; + } + + public abstract void updateFooter(FooterPreference fp); + + public void setLearnMoreLink(FooterPreference p, String url) { + p.setLearnMoreAction(v -> { + var i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(i); + }); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getPrefContext()); + setPreferenceScreen(screen); + requireActivity().setTitle(getTitle()); + } + + protected abstract CharSequence getTitle(); + + @Nullable + private SelectorWithWidgetPreference[] radioButtons; + @Nullable + private FooterPreference footer; + + private Preference[] extraPrefs; + + @Override + protected boolean refreshUi() { + Entry[] entries = getEntries(); + int entryCnt = entries.length; + + Context ctx = getPrefContext(); + PreferenceScreen screen = getPreferenceScreen(); + + if (radioButtons == null || radioButtons.length != entryCnt) { + if (radioButtons != null) { + for (Preference p : radioButtons) { + screen.removePreference(p); + } + } + + radioButtons = new SelectorWithWidgetPreference[entryCnt]; + + for (int i = 0; i < entryCnt; ++i) { + var p = new SelectorWithWidgetPreference(ctx); + p.setOnClickListener(this); + screen.addPreference(p); + radioButtons[i] = p; + } + } + + for (int i = 0; i < entryCnt; ++i) { + SelectorWithWidgetPreference p = radioButtons[i]; + Entry e = entries[i]; + p.setKey(Integer.toString(e.id)); + p.setTitle(e.title); + p.setSummary(e.summary); + p.setEnabled(e.isEnabled); + p.setChecked(e.isChecked); + } + + List extraPrefModels = getExtraPrefModels(); + + if (extraPrefs == null) { + if (extraPrefModels.isEmpty()) { + extraPrefs = new Preference[0]; + } else { + var spacer = new SpacePreference(ctx, null); + int h = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 48f, ctx.getResources().getDisplayMetrics()); + spacer.setHeight(h); + screen.addPreference(spacer); + + extraPrefs = new Preference[extraPrefModels.size()]; + for (int i = 0; i < extraPrefs.length; ++i) { + PrefModel pm = extraPrefModels.get(i); + Preference p; + if (pm instanceof LinkPrefModel lpm) { + p = new Preference(ctx); + p.setOnPreferenceClickListener(pref -> { + lpm.onClick.run(); + return true; + }); + } else if (pm instanceof TogglePrefModel tpm) { + p = new SwitchPreferenceCompat(ctx); + p.setOnPreferenceChangeListener(tpm.onChangeListener); + } else { + throw new IllegalArgumentException(pm.toString()); + } + p.setKey("extra_pref_" + i); + screen.addPreference(p); + extraPrefs[i] = p; + } + } + } + for (int i = 0; i < extraPrefs.length; ++i) { + PrefModel pm = extraPrefModels.get(i); + Preference p = extraPrefs[i]; + if (pm instanceof TogglePrefModel tpm) { + var sp = (SwitchPreferenceCompat) p; + sp.setChecked(tpm.isChecked); + } + p.setTitle(pm.title); + p.setSummary(pm.summary); + p.setEnabled(pm.isEnabled); + } + + if (hasFooter()) { + if (footer == null) { + footer = new FooterPreference(ctx); + screen.addPreference(footer); + } + updateFooter(footer); + } else { + if (footer != null) { + screen.removePreference(footer); + footer = null; + } + } + + return true; + } + + public static class PrefModel { + public CharSequence title; + public CharSequence summary; + public boolean isEnabled = true; + } + + public static class LinkPrefModel extends PrefModel { + public Runnable onClick; + } + + public static class TogglePrefModel extends PrefModel { + public boolean isChecked; + public Preference.OnPreferenceChangeListener onChangeListener; + } + + protected List getExtraPrefModels() { + return Collections.emptyList(); + } + + @Override + public final void onRadioButtonClicked(SelectorWithWidgetPreference emiter) { + int id = Integer.parseInt(emiter.getKey()); + onEntrySelected(id); + if (!refreshUi()) { + setIntentAndFinish(true); + } + } + + public abstract void onEntrySelected(int id); + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + return null; + } + + @Override + public int getMetricsCategory() { + return METRICS_CATEGORY_UNKNOWN; + } + + protected ApplicationInfo getAppInfo() { + return mPackageInfo.applicationInfo; + } +} From b9b5995e38cb0ad891e9741eae42acd255298a2a Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 15 Nov 2023 13:44:23 +0200 Subject: [PATCH 066/128] infrastructure for AppSwitch UIs Squashed with 28baa8dcbf5a0d285d8bf3852b723beaa290a973 by quh4gko8 <88831734+quh4gko8@users.noreply.github.com> --- res/values/strings_app_exploit_protection.xml | 37 +++ .../settings/applications/AswAdapter.kt | 88 +++++++ .../applications/AswAppInfoFragment.java | 228 ++++++++++++++++++ .../AswAppListPrefController.java | 27 +++ .../AswExploitProtectionFragment.java | 44 ++++ .../spa/app/appinfo/AppInfoSettings.kt | 3 + .../settings/spa/app/appinfo/AswAppList.kt | 101 ++++++++ .../settings/spa/app/appinfo/AswPreference.kt | 27 +++ .../spa/app/appinfo/PackageInfoPresenter.kt | 5 +- 9 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 res/values/strings_app_exploit_protection.xml create mode 100644 src/com/android/settings/applications/AswAdapter.kt create mode 100644 src/com/android/settings/applications/AswAppInfoFragment.java create mode 100644 src/com/android/settings/applications/AswAppListPrefController.java create mode 100644 src/com/android/settings/applications/AswExploitProtectionFragment.java create mode 100644 src/com/android/settings/spa/app/appinfo/AswAppList.kt create mode 100644 src/com/android/settings/spa/app/appinfo/AswPreference.kt diff --git a/res/values/strings_app_exploit_protection.xml b/res/values/strings_app_exploit_protection.xml new file mode 100644 index 00000000000..5081ee0a037 --- /dev/null +++ b/res/values/strings_app_exploit_protection.xml @@ -0,0 +1,37 @@ + + + Exploit protection + + Allowed + Blocked + Default (%1$s) + Disabled + Enabled + Restricted + + Warning + This setting will weaken exploit protections for the selected app. + Proceed + + Default value can be changed in the %1$s section + + The selected app is known to be compatible with this setting + %1$s by default, because this app is known to need it. +This behavior can be disabled in the %2$s section. + + %1$s by default because this app depends on Play services + + Block for third-party apps by default + Restrict for third-party apps by default + Disable for third-party apps by default + Restricted by default + Restricted by default, except for apps that depend on Play services + Disabled by default + Blocked by default + Allowed for third-party apps by default + Enabled for third-party apps by default + + %1$s is enabled + + Exploit protection compatibility mode + diff --git a/src/com/android/settings/applications/AswAdapter.kt b/src/com/android/settings/applications/AswAdapter.kt new file mode 100644 index 00000000000..478dd02e25e --- /dev/null +++ b/src/com/android/settings/applications/AswAdapter.kt @@ -0,0 +1,88 @@ +package com.android.settings.applications + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.ext.settings.app.AppSwitch +import androidx.preference.Preference +import androidx.preference.PreferenceScreen +import com.android.settings.R +import com.android.settings.SettingsPreferenceFragment +import com.android.settings.applications.appinfo.AppInfoDashboardFragment +import com.android.settings.spa.SpaActivity.Companion.startSpaActivity +import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider +import com.android.settings.spa.app.appinfo.AswAppListPageProvider +import com.android.settingslib.spaprivileged.model.app.userId +import kotlin.reflect.KClass + +abstract class AswAdapter { + abstract fun getAppSwitch(): T + + enum class Category { + ExploitProtection, + } + + fun getCategory(): Category = Category.ExploitProtection + + fun getPreferenceSummary(ctx: Context, appInfo: ApplicationInfo): CharSequence { + val asw = getAppSwitch() + val si = AppSwitch.StateInfo() + val userId = appInfo.userId + val isOn = asw.get(ctx, userId, appInfo, GosPackageState.get(appInfo.packageName, userId), si) + return if (si.isUsingDefaultValue) { + getDefaultTitle(ctx, isOn) + } else { + if (isOn) getOnTitle(ctx) else getOffTitle(ctx) + } + } + + open fun getNotificationToggleTitle(ctx: Context): CharSequence? = null + open fun getNotificationToggleSummary(ctx: Context): CharSequence? = null + + abstract fun getAswTitle(ctx: Context): CharSequence + + open fun getShortAswTitle(ctx: Context) = getAswTitle(ctx) + + fun getDefaultTitle(ctx: Context, isOn: Boolean): CharSequence { + return ctx.getString(R.string.aep_default, + if (isOn) getOnTitle(ctx) else getOffTitle(ctx) + ) + } + + open fun getOnTitle(ctx: Context): CharSequence = ctx.getText(R.string.aep_enabled) + + open fun getOffTitle(ctx: Context): CharSequence = ctx.getText(R.string.aep_disabled) + + abstract fun getDetailFragmentClass(): KClass + + fun openAppPage(context: Context, app: ApplicationInfo) { + AppInfoDashboardFragment.startAppInfoFragment( + getDetailFragmentClass().java, + app, context, AppInfoSettingsProvider.METRICS_CATEGORY, + ) + } + + fun makeAppListPageProvider() = AswAppListPageProvider(this) + + fun getAppListPageProviderName() = this::class.simpleName!! + + open fun shouldIncludeInAppListPage(app: ApplicationInfo, gosPs: GosPackageState) = true + + fun openAppListPage(ctx: Context) { + ctx.startSpaActivity(getAppListPageProviderName()) + } + + @JvmOverloads + fun addAppListPageLink(screen: PreferenceScreen, + title: CharSequence = screen.context.getText(R.string.default_see_all_apps_title)) { + val ctx = screen.context + val pref = Preference(ctx).apply { + this.title = title + setOnPreferenceClickListener { p -> + p.context.startSpaActivity(getAppListPageProviderName()) + true + } + } + screen.addPreference(pref) + } +} diff --git a/src/com/android/settings/applications/AswAppInfoFragment.java b/src/com/android/settings/applications/AswAppInfoFragment.java new file mode 100644 index 00000000000..cac5229f781 --- /dev/null +++ b/src/com/android/settings/applications/AswAppInfoFragment.java @@ -0,0 +1,228 @@ +package com.android.settings.applications; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.GosPackageState; +import android.ext.settings.app.AppSwitch; +import android.os.Bundle; + +import androidx.annotation.Nullable; + +import com.android.settings.R; +import com.android.settings.applications.appinfo.RadioButtonAppInfoFragment; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AswAppInfoFragment + extends RadioButtonAppInfoFragment { + + protected static final int ID_DEFAULT = 0; + protected static final int ID_ON = 1; + protected static final int ID_OFF = 2; + + public abstract AswAdapter getAswAdapter(); + + protected AswAdapter adapter; + private final ArrayList extraPrefs = new ArrayList<>(); + @Nullable + private TogglePrefModel notifTogglePref; + + @Override + public void onCreate(Bundle savedInstanceState) { + adapter = getAswAdapter(); + super.onCreate(savedInstanceState); + + if (adapter.getAppSwitch().hasNotification()) { + Context ctx = requireContext(); + var p = new TogglePrefModel(); + p.title = adapter.getNotificationToggleTitle(ctx); + p.summary = adapter.getNotificationToggleSummary(ctx); + p.onChangeListener = (pref, state) -> { + var ed = GosPackageState.edit(mPackageName, mUserId); + adapter.getAppSwitch().setNotificationEnabled(ed, (boolean) state); + return ed.apply(); + }; + extraPrefs.add(p); + notifTogglePref = p; + } + { + var p = new LinkPrefModel(); + p.title = getText(R.string.default_see_all_apps_title); + p.onClick = () -> adapter.openAppListPage(requireContext()); + extraPrefs.add(p); + } + } + + @Override + protected CharSequence getTitle() { + return adapter.getAswTitle(requireContext()); + } + + @Override + public Entry[] getEntries() { + Context ctx = requireContext(); + AppSwitch asw = adapter.getAppSwitch(); + + int userId = mUserId; + GosPackageState ps = GosPackageState.get(mPackageName, userId); + ApplicationInfo appInfo = getAppInfo(); + var si = new AppSwitch.StateInfo(); + boolean state = asw.get(ctx, userId, appInfo, ps, si); + + boolean isDefault = si.isUsingDefaultValue(); + boolean isImmutable = si.isImmutable(); + + var defaultSi = new AppSwitch.StateInfo(); + boolean defaultValue = asw.getDefaultValue(ctx, userId, appInfo, ps, defaultSi); + + var def = createEntry(ID_DEFAULT, adapter.getDefaultTitle(ctx, defaultValue)); + def.isChecked = isDefault; + def.isEnabled = !isImmutable; + if (def.isEnabled) { + int dvr = defaultSi.getDefaultValueReason(); + CharSequence summary = getSummaryForDefaultValueReason(dvr); + if (summary == null) { + summary = switch (dvr) { + case AppSwitch.DVR_DEFAULT_SETTING -> { + if (!ctx.getUser().isSystem()) { + yield null; + } + yield getString(R.string.aep_dvr_default_security_setting, + getAppDefaultSettingPathForCategory(adapter.getCategory())); + } + case AppSwitch.DVR_APP_COMPAT_CONFIG_HARDENING_OPT_IN -> + getText(R.string.aep_dvr_compat_config_hardening_opt_in); + case AppSwitch.DVR_APP_COMPAT_CONFIG_HARDENING_OPT_OUT -> { + if (!ctx.getUser().isSystem()) { + yield null; + } + var s = defaultValue ? adapter.getOnTitle(ctx) : adapter.getOffTitle(ctx); + yield getString(R.string.aep_dvr_compat_config_hardening_opt_out, + s.toString(), getSettingPath(R.string.safety_center_title, + R.string.more_security_privacy_settings)); + } + case AppSwitch.DVR_APP_IS_CLIENT_OF_GMSCORE -> { + var s = defaultValue ? adapter.getOnTitle(ctx) : adapter.getOffTitle(ctx); + yield getString(R.string.aep_dvr_app_is_client_of_gmscore, s.toString()); + } + default -> null; + }; + } + def.summary = summary; + } + + var enabled = createEntry(ID_ON, adapter.getOnTitle(ctx)); + enabled.isChecked = !isDefault && state; + enabled.isEnabled = enabled.isChecked || !isImmutable; + + var disabled = createEntry(ID_OFF, adapter.getOffTitle(ctx)); + disabled.isChecked = !isDefault && !state; + disabled.isEnabled = disabled.isChecked || !isImmutable; + + if (isImmutable) { + int immutabilityReason = si.getImmutabilityReason(); + CharSequence summary = getSummaryForImmutabilityReason(immutabilityReason); + if (summary == null) { + if (immutabilityReason == AppSwitch.IR_EXPLOIT_PROTECTION_COMPAT_MODE) { + summary = getString(R.string.aep_ir_exploit_protection_compat_mode, + getString(R.string.aep_compat_mode_title)); + } + } + if (enabled.isChecked) { + enabled.summary = summary; + } + if (disabled.isChecked) { + disabled.summary = summary; + } + } + + if (notifTogglePref != null) { + notifTogglePref.isChecked = adapter.getAppSwitch().isNotificationEnabled(ps); + notifTogglePref.isEnabled = state; + } + + return new Entry[] { def, enabled, disabled }; + } + + private String getAppDefaultSettingPathForCategory(AswAdapter.Category category) { + switch (category) { + case ExploitProtection: + return getSettingPath(R.string.safety_center_title, R.string.exploit_protection_settings); + default: + throw new IllegalArgumentException(category.toString()); + } + } + + private String getSettingPath(int... parts) { + var sb = new StringBuilder(); + for (int i = 0; i < parts.length; ++i) { + if (i != 0) { + sb.append(" > "); + } + sb.append(getString(parts[i])); + } + return sb.toString(); + } + + @Nullable + protected CharSequence getSummaryForDefaultValueReason(int dvr) { + return null; + } + + @Nullable + protected CharSequence getSummaryForImmutabilityReason(int ir) { + return null; + } + + @Override + protected List getExtraPrefModels() { + return extraPrefs; + } + + @Override + public final void onEntrySelected(int id) { + Context ctx = requireContext(); + AppSwitch asw = adapter.getAppSwitch(); + int userId = mUserId; + String pkgName = mPackageName; + GosPackageState ps = GosPackageState.get(pkgName, userId); + ApplicationInfo appInfo = getAppInfo(); + + boolean isImmutable = asw.isImmutable(ctx, userId, appInfo, ps); + + if (isImmutable) { + return; + } + + Runnable r = () -> { + GosPackageState.Editor ed = GosPackageState.edit(pkgName, userId); + + switch (id) { + case ID_DEFAULT -> asw.setUseDefaultValue(ed); + case ID_ON, ID_OFF -> asw.set(ed, id == ID_ON); + default -> throw new IllegalStateException(); + } + + ed.setKillUidAfterApply(shouldKillUidAfterChange()); + + if (!ed.apply()) { + finish(); + } + + if (!refreshUi()) { + setIntentAndFinish(true); + } + }; + + completeStateChange(id, asw.get(ctx, userId, appInfo, ps), r); + } + + protected void completeStateChange(int newEntryId, boolean curValue, Runnable stateChangeAction) { + stateChangeAction.run(); + } + + protected boolean shouldKillUidAfterChange() { + return true; + } +} diff --git a/src/com/android/settings/applications/AswAppListPrefController.java b/src/com/android/settings/applications/AswAppListPrefController.java new file mode 100644 index 00000000000..c6d9fa7260a --- /dev/null +++ b/src/com/android/settings/applications/AswAppListPrefController.java @@ -0,0 +1,27 @@ +package com.android.settings.applications; + +import android.content.Context; + +import androidx.preference.Preference; + +import com.android.settings.applications.AswAdapter; +import com.android.settings.core.BasePreferenceController; + +public abstract class AswAppListPrefController extends BasePreferenceController { + + protected final AswAdapter aswAdapter; + + public AswAppListPrefController(Context context, String preferenceKey, AswAdapter adapter) { + super(context, preferenceKey); + aswAdapter = adapter; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!getPreferenceKey().equals(preference.getKey())) { + return false; + } + aswAdapter.openAppListPage(preference.getContext()); + return true; + } +} diff --git a/src/com/android/settings/applications/AswExploitProtectionFragment.java b/src/com/android/settings/applications/AswExploitProtectionFragment.java new file mode 100644 index 00000000000..19d0a470433 --- /dev/null +++ b/src/com/android/settings/applications/AswExploitProtectionFragment.java @@ -0,0 +1,44 @@ +package com.android.settings.applications; + +import android.content.Context; +import android.content.pm.GosPackageState; +import android.ext.settings.app.AppSwitch; + +import androidx.appcompat.app.AlertDialog; + +import com.android.settings.R; + +public abstract class AswExploitProtectionFragment extends AswAppInfoFragment { + + @Override + protected void completeStateChange(int newEntryId, boolean curValue, Runnable stateChangeAction) { + Context ctx = requireContext(); + + boolean showWarning = false; + if (curValue) { + if (newEntryId == ID_OFF) { + showWarning = true; + } else if (newEntryId == ID_DEFAULT) { + AppSwitch asw = adapter.getAppSwitch(); + int userId = mUserId; + var ps = GosPackageState.get(mPackageName, userId); + showWarning = !asw.getDefaultValue(ctx, userId, getAppInfo(), ps); + } + } + if (showWarning) { + var d = getExploitProtectionWarningOnDisable(ctx, stateChangeAction); + d.show(); + } else { + stateChangeAction.run(); + } + } + + public static AlertDialog.Builder getExploitProtectionWarningOnDisable(Context ctx, Runnable action) { + var b = new AlertDialog.Builder(ctx); + b.setTitle(R.string.aep_confirm_disable_title); + b.setMessage(R.string.aep_confirm_disable_warning_msg); + b.setNegativeButton(R.string.cancel, null); + b.setPositiveButton(R.string.aep_confirm_disable_proceed_btn, (d, w) -> action.run()); + return b; + } +} diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index cfe8f368bf5..7431e928e0a 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -45,6 +45,7 @@ import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListPro import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider import com.android.settings.spa.app.specialaccess.WriteSystemPreferencesAppListProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.LifecycleEffect import com.android.settingslib.spa.framework.compose.navigator import com.android.settingslib.spa.widget.scaffold.RegularScaffold import com.android.settingslib.spa.widget.ui.Category @@ -122,6 +123,8 @@ object AppInfoSettingsProvider : SettingsPageProvider { @Composable private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { + // needed to refresh AppSwitch items after returning from their fragments + LifecycleEffect(onStart = { check(packageInfoPresenter.manualRefreshFlow.tryEmit(Any())) }) val packageInfoState = packageInfoPresenter.flow.collectAsStateWithLifecycle() val featureFlags: PmFeatureFlags = PmFeatureFlagsImpl() RegularScaffold( diff --git a/src/com/android/settings/spa/app/appinfo/AswAppList.kt b/src/com/android/settings/spa/app/appinfo/AswAppList.kt new file mode 100644 index 00000000000..c08586a7002 --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AswAppList.kt @@ -0,0 +1,101 @@ +package com.android.settings.spa.app.appinfo + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.os.Bundle +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import com.android.settings.applications.AswAdapter +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.rememberContext +import com.android.settingslib.spa.framework.util.asyncFilter +import com.android.settingslib.spa.framework.util.asyncMap +import com.android.settingslib.spa.widget.ui.SpinnerOption +import com.android.settingslib.spaprivileged.model.app.AppListModel +import com.android.settingslib.spaprivileged.model.app.AppRecord +import com.android.settingslib.spaprivileged.template.app.AppListItem +import com.android.settingslib.spaprivileged.template.app.AppListItemModel +import com.android.settingslib.spaprivileged.template.app.AppListPage +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +open class AswAppListPageProvider(val adapter: AswAdapter<*>) : SettingsPageProvider { + override val name = adapter.getAppListPageProviderName() + + @Composable + override fun Page(arguments: Bundle?) { + AswAppListPage(adapter) + } +} + +@Composable +fun AswAppListPage(adapter: AswAdapter<*>) { + AppListPage( + title = rememberContext { ctx -> + adapter.getShortAswTitle(ctx).toString() + }, + listModel = rememberContext { ctx -> + AswAppListModel(ctx, adapter) + }, + ) +} + +class AppRecordImpl(override val app: ApplicationInfo, val gosPackageState: GosPackageState) : AppRecord + +class AswAppListModel(val ctx: Context, val adapter: AswAdapter<*>) : AppListModel { + companion object { + const val SPINNER_OPTION_OFF = 0 + const val SPINNER_OPTION_ON = 1 + } + + override fun getSpinnerOptions(recordList: List): List { + val options = ArrayList(2) + options.add(SpinnerOption(SPINNER_OPTION_OFF, adapter.getOffTitle(ctx).toString())) + options.add(SpinnerOption(SPINNER_OPTION_ON, adapter.getOnTitle(ctx).toString())) + return options + } + + override fun transform( + userIdFlow: Flow, + appListFlow: Flow> + ): Flow> { + return userIdFlow.combine(appListFlow) { userId, appList -> + appList.asyncMap { app -> + val gosPs = GosPackageState.get(app.packageName, userId) + if (adapter.shouldIncludeInAppListPage(app, gosPs)) { + AppRecordImpl(app, GosPackageState.get(app.packageName, userId)) + } else { + null + } + }.filterNotNull() + } + } + + override fun filter( + userIdFlow: Flow, + option: Int, + recordListFlow: Flow> + ): Flow> { + val appSwitch = adapter.getAppSwitch() + return userIdFlow.combine(recordListFlow) { userId, recordList -> + recordList.asyncFilter { + val value = appSwitch.get(ctx, userId, it.app, it.gosPackageState) + when (option) { + SPINNER_OPTION_OFF -> !value + SPINNER_OPTION_ON -> value + else -> throw IllegalArgumentException(option.toString()) + } + } + } + } + + @Composable + override fun AppListItemModel.AppItem() { + val context = LocalContext.current + AppListItem(onClick = { + adapter.openAppPage(context, record.app) + }) + } +} + diff --git a/src/com/android/settings/spa/app/appinfo/AswPreference.kt b/src/com/android/settings/spa/app/appinfo/AswPreference.kt new file mode 100644 index 00000000000..116fa047025 --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AswPreference.kt @@ -0,0 +1,27 @@ +package com.android.settings.spa.app.appinfo + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.ext.settings.app.AppSwitch +import androidx.compose.runtime.Composable +import com.android.settings.applications.AswAdapter +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spaprivileged.model.app.installed + +@Composable +fun AswPreference(context: Context, app: ApplicationInfo, adapter: AswAdapter) { + if (!app.installed) { + return + } + + Preference(object : PreferenceModel { + override val title = adapter.getAswTitle(context).toString() + override val summary = { + adapter.getPreferenceSummary(context, app).toString() + } + override val onClick = { + adapter.openAppPage(context, app) + } + }) +} diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt index 36fe93e2f27..dc650d78883 100644 --- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt @@ -44,6 +44,7 @@ import com.android.settingslib.spaprivileged.model.app.IPackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter @@ -96,7 +97,9 @@ class PackageInfoPresenter( intent.action != Intent.ACTION_PACKAGE_REMOVED || intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false) - val flow: StateFlow = merge(flowOf(null), appChangeFlow) + val manualRefreshFlow = MutableStateFlow(null) + + val flow: StateFlow = merge(flowOf(null), appChangeFlow, manualRefreshFlow) .map { getPackageInfo() } .stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Eagerly, null) From bae7cfc1f304b6ba0808b86deda45dc7bb20b04f Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 2 Mar 2024 12:09:48 +0200 Subject: [PATCH 067/128] add "Exploit protection" category to App info --- src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 7431e928e0a..0e84f1b30d7 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -178,6 +178,9 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { Enable16KbAppCompatPreference(app) } + Category(title = stringResource(R.string.exploit_protection_category_title)) { + } + Category(title = stringResource(R.string.app_install_details_group_title)) { AppInstallerInfoPreference(app) } From 319d55eb73acee1b2a324124b507bce64213b82a Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Tue, 26 Sep 2023 10:34:50 +0300 Subject: [PATCH 068/128] add per-app setting for exploit protection compatibility mode --- res/values/strings_app_exploit_protection.xml | 1 + ...oitProtectionCompatModeSwitchPreference.kt | 59 +++++++++++++++++++ .../spa/app/appinfo/AppInfoSettings.kt | 1 + 3 files changed, 61 insertions(+) create mode 100644 src/com/android/settings/spa/app/appinfo/AppExploitProtectionCompatModeSwitchPreference.kt diff --git a/res/values/strings_app_exploit_protection.xml b/res/values/strings_app_exploit_protection.xml index 5081ee0a037..ed4eef3ab79 100644 --- a/res/values/strings_app_exploit_protection.xml +++ b/res/values/strings_app_exploit_protection.xml @@ -34,4 +34,5 @@ This behavior can be disabled in the %2$s section. %1$s is enabled Exploit protection compatibility mode + Improves compatibility by disabling some of the GrapheneOS exploit protections for this app diff --git a/src/com/android/settings/spa/app/appinfo/AppExploitProtectionCompatModeSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/AppExploitProtectionCompatModeSwitchPreference.kt new file mode 100644 index 00000000000..daf3ace3dba --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AppExploitProtectionCompatModeSwitchPreference.kt @@ -0,0 +1,59 @@ +package com.android.settings.spa.app.appinfo + +import android.content.pm.ApplicationInfo +import android.content.pm.GosPackageState +import android.content.pm.GosPackageStateFlag +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settings.applications.AswExploitProtectionFragment +import com.android.settingslib.spa.widget.preference.SwitchPreference +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import com.android.settingslib.spaprivileged.model.app.installed +import com.android.settingslib.spaprivileged.model.app.userId + +@Composable +fun AppExploitProtectionCompatModeSwitchPreference(app: ApplicationInfo, + packageInfoPresenter: PackageInfoPresenter) { + if (app.isSystemApp || !app.installed) { + return + } + + val context = LocalContext.current + val pkgName = app.packageName + val userId = app.userId + val psFlag = GosPackageStateFlag.ENABLE_EXPLOIT_PROTECTION_COMPAT_MODE + + SwitchPreference(object : SwitchPreferenceModel { + override val title = stringResource(R.string.aep_compat_mode_title) + override val summary = { + context.getString(R.string.aep_compat_mode_summary) + } + + override val checked = { + GosPackageState.get(pkgName, userId).hasFlag(psFlag) + } + + override val onCheckedChange = { newChecked: Boolean -> + val action = Runnable { + GosPackageState.edit(pkgName, userId).run { + setFlagState(psFlag, newChecked) + killUidAfterApply() + if (apply()) { + // needed to recompose other exploit protection settings + check(packageInfoPresenter.manualRefreshFlow.tryEmit(Any())) + } + } + } + + if (newChecked) { + val d = AswExploitProtectionFragment.getExploitProtectionWarningOnDisable(context, action) + d.show() + } else { + action.run() + } + Unit + } + }) +} diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 0e84f1b30d7..8f508b9f4ad 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -179,6 +179,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { } Category(title = stringResource(R.string.exploit_protection_category_title)) { + AppExploitProtectionCompatModeSwitchPreference(app, packageInfoPresenter) } Category(title = stringResource(R.string.app_install_details_group_title)) { From 0c8ebbb715449892df7179fc0d2b070ae212a836 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 7 Oct 2023 21:28:08 +0300 Subject: [PATCH 069/128] add toggle for automatic exploit protection compatibility mode --- res/values/strings_ext.xml | 6 ++++++ res/xml/more_security_privacy_settings.xml | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index 652d423f59c..edd4090895a 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -118,6 +118,12 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. Allow running in background + Automatic exploit protection compatibility mode + + +"Allow automatically disabling exploit protections for apps that are known to need it" + + "Warning: enabling this setting might cause some apps to stop working. diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml index c1b98843a45..99a56dd9ef1 100644 --- a/res/xml/more_security_privacy_settings.xml +++ b/res/xml/more_security_privacy_settings.xml @@ -237,6 +237,13 @@ settings:keywords="@string/keywords_app_pinning" settings:controller="com.android.settings.security.ScreenPinningPreferenceController" /> + + Date: Thu, 16 Nov 2023 17:23:47 +0200 Subject: [PATCH 070/128] add per-app setting for hardened_malloc --- AndroidManifest.xml | 18 +++++++ res/values/strings_app_exploit_protection.xml | 8 +++ res/xml/exploit_protection_settings.xml | 5 ++ src/com/android/settings/Settings.java | 2 + .../applications/AppHardenedMalloc.kt | 54 +++++++++++++++++++ .../core/gateway/SettingsGateway.java | 1 + .../settings/spa/SettingsSpaEnvironment.kt | 1 + .../spa/app/appinfo/AppInfoSettings.kt | 1 + 8 files changed, 90 insertions(+) create mode 100644 src/com/android/settings/applications/AppHardenedMalloc.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 101e37fc778..18d64b27ab1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5475,5 +5475,23 @@ + + + + + + + + + + + + + diff --git a/res/values/strings_app_exploit_protection.xml b/res/values/strings_app_exploit_protection.xml index ed4eef3ab79..8581d72090b 100644 --- a/res/values/strings_app_exploit_protection.xml +++ b/res/values/strings_app_exploit_protection.xml @@ -35,4 +35,12 @@ This behavior can be disabled in the %2$s section. Exploit protection compatibility mode Improves compatibility by disabling some of the GrapheneOS exploit protections for this app + + Hardened memory allocator + + Hardened memory allocator (hardened_malloc) provides substantial defenses against the most common classes of vulnerabilities (heap memory corruption) along with reducing the lifetime of sensitive data in memory. + This app doesn’t have native code + This is a 32-bit app, hardened_malloc is 64-bit only + hardened_malloc is not allowed to be disabled for a preinstalled app + This is a debuggable app, hardened_malloc can’t be disabled diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index ada50481305..34d6ccf6cb4 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -24,6 +24,11 @@ android:key="app_exploit_protection_settings" android:title="@string/app_exploit_protection_category"> + + () { + + override fun getAppSwitch() = AswUseHardenedMalloc.I + + override fun getAswTitle(ctx: Context) = ctx.getText(R.string.aep_hmalloc) + + override fun getDetailFragmentClass() = AppHardenedMallocFragment::class +} + +@Composable +fun AppHardenedMallocPreference(app: ApplicationInfo) { + val context = LocalContext.current + AswPreference(context, app, AswAdapterUseHardenedMalloc) +} + +class AppHardenedMallocFragment : AswExploitProtectionFragment() { + + override fun getAswAdapter() = AswAdapterUseHardenedMalloc + + override fun getSummaryForImmutabilityReason(ir: Int): CharSequence? { + val id = when (ir) { + AppSwitch.IR_IS_SYSTEM_APP -> R.string.aep_hmalloc_ir_preinstalled_app + AppSwitch.IR_NO_NATIVE_CODE -> R.string.aep_hmalloc_ir_no_native_code + AppSwitch.IR_NON_64_BIT_NATIVE_CODE -> R.string.aep_hmalloc_ir_32_bit_native_code + AppSwitch.IR_IS_DEBUGGABLE_APP -> R.string.aep_hmalloc_ir_debuggable_app + else -> return null + } + return getText(id) + } + + override fun updateFooter(fp: FooterPreference) { + fp.setTitle(R.string.aep_hmalloc_footer) + setLearnMoreLink(fp, "https://grapheneos.org/features#exploit-mitigations") + } +} + + +class HardenedMallocAppListPrefController(context: Context, preferenceKey: String) : + AswAppListPrefController(context, preferenceKey, AswAdapterUseHardenedMalloc) { + + override fun getAvailabilityStatus() = AVAILABLE +} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 4dee4dc0301..348e8b9a708 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -218,6 +218,7 @@ public class SettingsGateway { * security exception if the fragment it needs to display is not in this list. */ public static final String[] ENTRY_FRAGMENTS = { + com.android.settings.applications.AppHardenedMallocFragment.class.getName(), com.android.settings.safetycenter.ExploitProtectionFragment.class.getName(), AdvancedConnectedDeviceDashboardFragment.class.getName(), CreateShortcut.class.getName(), diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index 7702db6bcde..1dd6f84c662 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -102,6 +102,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { open fun settingsPageProviders() = listOf( HomePageProvider, AppsMainPageProvider, + com.android.settings.applications.AswAdapterUseHardenedMalloc.makeAppListPageProvider(), AllAppListPageProvider, AppInfoSettingsProvider, SpecialAppAccessPageProvider, diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 8f508b9f4ad..0415c9c0392 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -180,6 +180,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { Category(title = stringResource(R.string.exploit_protection_category_title)) { AppExploitProtectionCompatModeSwitchPreference(app, packageInfoPresenter) + com.android.settings.applications.AppHardenedMallocPreference(app) } Category(title = stringResource(R.string.app_install_details_group_title)) { From a544cd76f011a1ec9f267907d1d3d7ca0336c8de Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Fri, 30 Aug 2024 16:23:56 +0300 Subject: [PATCH 071/128] add dev mode per-app setting for extended virtual address space --- res/values/strings_app_exploit_protection.xml | 5 ++ res/xml/exploit_protection_settings.xml | 5 ++ .../applications/AppExtendedVaSpace.kt | 58 +++++++++++++++++++ .../settings/spa/SettingsSpaEnvironment.kt | 1 + .../spa/app/appinfo/AppInfoSettings.kt | 1 + 5 files changed, 70 insertions(+) create mode 100644 src/com/android/settings/applications/AppExtendedVaSpace.kt diff --git a/res/values/strings_app_exploit_protection.xml b/res/values/strings_app_exploit_protection.xml index 8581d72090b..d9d94fe5484 100644 --- a/res/values/strings_app_exploit_protection.xml +++ b/res/values/strings_app_exploit_protection.xml @@ -43,4 +43,9 @@ This behavior can be disabled in the %2$s section. This is a 32-bit app, hardened_malloc is 64-bit only hardened_malloc is not allowed to be disabled for a preinstalled app This is a debuggable app, hardened_malloc can’t be disabled + + Extended virtual address space + Extends virtual address space from 39 to 48 bits, which significantly improves the address space layout randomization. + Incompatible with 32-bit apps + Required by hardened_malloc diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index 34d6ccf6cb4..1068040a7a6 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -29,6 +29,11 @@ android:title="@string/aep_hmalloc" settings:controller="com.android.settings.applications.HardenedMallocAppListPrefController" /> + + () { + + override fun getAppSwitch() = AswUseExtendedVaSpace.I + + override fun getAswTitle(ctx: Context) = ctx.getText(R.string.aep_ext_va_space) + + override fun getDetailFragmentClass() = AppExtendedVaSpaceFragment::class +} + +@Composable +fun AppExtendedVaSpacePreference(app: ApplicationInfo) { + val context = LocalContext.current + if (ExtSettingControllerHelper.getDevModeSettingAvailability(context) != BasePreferenceController.AVAILABLE) { + return + } + + AswPreference(context, app, AswAdapterUseExtendedVaSpace) +} + +class AppExtendedVaSpaceFragment : AswExploitProtectionFragment() { + + override fun getAswAdapter() = AswAdapterUseExtendedVaSpace + + override fun getSummaryForImmutabilityReason(ir: Int): CharSequence? { + val id = when (ir) { + AppSwitch.IR_REQUIRED_BY_HARDENED_MALLOC -> R.string.aep_ext_va_space_ir_hardened_malloc + AppSwitch.IR_NON_64_BIT_NATIVE_CODE -> R.string.aep_ext_va_space_ir_32_bit_native_code + else -> return null + } + return getText(id) + } + + override fun updateFooter(fp: FooterPreference) { + fp.setTitle(R.string.aep_ext_va_space_footer) + } +} + + +class ExtendedVaSpaceAppListPrefController(context: Context, preferenceKey: String) : + AswAppListPrefController(context, preferenceKey, AswAdapterUseExtendedVaSpace) { + + override fun getAvailabilityStatus() = ExtSettingControllerHelper + .getDevModeSettingAvailability(mContext) +} diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index 1dd6f84c662..e001c8b152a 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -103,6 +103,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { HomePageProvider, AppsMainPageProvider, com.android.settings.applications.AswAdapterUseHardenedMalloc.makeAppListPageProvider(), + com.android.settings.applications.AswAdapterUseExtendedVaSpace.makeAppListPageProvider(), AllAppListPageProvider, AppInfoSettingsProvider, SpecialAppAccessPageProvider, diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 0415c9c0392..8954d4916da 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -181,6 +181,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { Category(title = stringResource(R.string.exploit_protection_category_title)) { AppExploitProtectionCompatModeSwitchPreference(app, packageInfoPresenter) com.android.settings.applications.AppHardenedMallocPreference(app) + com.android.settings.applications.AppExtendedVaSpacePreference(app) } Category(title = stringResource(R.string.app_install_details_group_title)) { From 28a43a0f79fcbde890e3fef6694df04e071f4bd0 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 15 Nov 2023 13:51:07 +0200 Subject: [PATCH 072/128] add per-app setting for native debugging --- AndroidManifest.xml | 18 ++++ res/values/strings_app_exploit_protection.xml | 8 ++ res/xml/exploit_protection_settings.xml | 11 +++ src/com/android/settings/Settings.java | 2 + .../applications/AppNativeDebugging.kt | 94 +++++++++++++++++++ .../core/gateway/SettingsGateway.java | 1 + .../settings/spa/SettingsSpaEnvironment.kt | 1 + .../spa/app/appinfo/AppInfoSettings.kt | 1 + 8 files changed, 136 insertions(+) create mode 100644 src/com/android/settings/applications/AppNativeDebugging.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 18d64b27ab1..a7101324453 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5476,6 +5476,24 @@ + + + + + + + + + + + + + Extends virtual address space from 39 to 48 bits, which significantly improves the address space layout randomization. Incompatible with 32-bit apps Required by hardened_malloc + + Native code debugging + Show notifications about blocked access attempts + +Native code debugging (ptrace) slightly weakens the app sandbox. Some apps (most notably banking apps) use it as a tampering detection tool. + + Native debugging is always blocked for preinstalled apps + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index 1068040a7a6..c56c38ef64c 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -34,6 +34,17 @@ android:title="@string/aep_ext_va_space" settings:controller="com.android.settings.applications.ExtendedVaSpaceAppListPrefController" /> + + + + () { + + override fun getAppSwitch() = AswDenyNativeDebug.I + + override fun getAswTitle(ctx: Context) = ctx.getText(R.string.aep_native_debug_title) + + override fun getOnTitle(ctx: Context) = ctx.getText(R.string.aep_blocked) + override fun getOffTitle(ctx: Context) = ctx.getText(R.string.aep_allowed) + + override fun getNotificationToggleTitle(ctx: Context) = ctx.getText(R.string.aep_native_debug_notif_toggle_title) + + override fun getDetailFragmentClass() = AppNativeDebuggingFragment::class +} + +@Composable +fun AppNativeDebuggingPreference(app: ApplicationInfo) { + val context = LocalContext.current + AswPreference(context, app, AswAdapterNativeDebugging) +} + +class AppNativeDebuggingFragment : AswExploitProtectionFragment() { + + override fun getAswAdapter() = AswAdapterNativeDebugging + + override fun getSummaryForImmutabilityReason(ir: Int): CharSequence? { + val id = when (ir) { + AppSwitch.IR_IS_SYSTEM_APP -> R.string.aep_native_debug_dvr_is_system_app + else -> return null + } + return getText(id) + } + + override fun updateFooter(fp: FooterPreference) { + fp.setTitle(R.string.aep_native_debug_footer) + } +} + +class AppDefaultNativeDebuggingPrefController(ctx: Context, key: String) : + BoolSettingFragmentPrefController(ctx, key, ExtSettings.ALLOW_NATIVE_DEBUG_BY_DEFAULT) { + + override fun getSummary(): CharSequence { + return resText(if (setting.get(mContext)) R.string.aep_default_summary_allowed_for_3p_apps else R.string.aep_default_summary_blocked) + } +} + +class AppDefaultNativeDebuggingFragment : BoolSettingFragment() { + + override fun getSetting(): BoolSetting { + invertSetting = true + return ExtSettings.ALLOW_NATIVE_DEBUG_BY_DEFAULT + } + + override fun getTitle() = getText(R.string.aep_native_debug_title) + + override fun getMainSwitchTitle() = getText(R.string.aep_default_main_switch_block_for_3p_apps) + + override fun addExtraPrefs(screen: PreferenceScreen) { + AswAdapterNativeDebugging.addAppListPageLink(screen) + } + + override fun makeFooterPref(builder: FooterPreference.Builder): FooterPreference { + val text = AppPrefUtils.getFooterForDefaultHardeningSetting( + requireContext(), + R.string.aep_native_debug_footer + ) + return builder.setTitle(text).build() + } +} + +class NativeDebuggingAppListPrefController(context: Context, preferenceKey: String) : + AswAppListPrefController(context, preferenceKey, AswAdapterNativeDebugging) { + + override fun getAvailabilityStatus() = ExtSettingControllerHelper + .getSecondaryUserOnlySettingAvailability(mContext) +} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 348e8b9a708..c69e658103d 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -218,6 +218,7 @@ public class SettingsGateway { * security exception if the fragment it needs to display is not in this list. */ public static final String[] ENTRY_FRAGMENTS = { + com.android.settings.applications.AppNativeDebuggingFragment.class.getName(), com.android.settings.applications.AppHardenedMallocFragment.class.getName(), com.android.settings.safetycenter.ExploitProtectionFragment.class.getName(), AdvancedConnectedDeviceDashboardFragment.class.getName(), diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index e001c8b152a..09c762f2fc5 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -104,6 +104,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { AppsMainPageProvider, com.android.settings.applications.AswAdapterUseHardenedMalloc.makeAppListPageProvider(), com.android.settings.applications.AswAdapterUseExtendedVaSpace.makeAppListPageProvider(), + com.android.settings.applications.AswAdapterNativeDebugging.makeAppListPageProvider(), AllAppListPageProvider, AppInfoSettingsProvider, SpecialAppAccessPageProvider, diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 8954d4916da..cce2f233c69 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -182,6 +182,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { AppExploitProtectionCompatModeSwitchPreference(app, packageInfoPresenter) com.android.settings.applications.AppHardenedMallocPreference(app) com.android.settings.applications.AppExtendedVaSpacePreference(app) + com.android.settings.applications.AppNativeDebuggingPreference(app) } Category(title = stringResource(R.string.app_install_details_group_title)) { From 30c525c514de8d1435a280e352dead195083c1be Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Fri, 30 Aug 2024 16:25:59 +0300 Subject: [PATCH 073/128] add per-app setting for memory tagging --- AndroidManifest.xml | 16 +++ res/values/strings_app_exploit_protection.xml | 18 +++ res/xml/exploit_protection_settings.xml | 11 ++ src/com/android/settings/Settings.java | 2 + .../settings/applications/AppMemoryTagging.kt | 111 ++++++++++++++++++ .../core/gateway/SettingsGateway.java | 1 + .../settings/spa/SettingsSpaEnvironment.kt | 1 + .../spa/app/appinfo/AppInfoSettings.kt | 1 + 8 files changed, 161 insertions(+) create mode 100644 src/com/android/settings/applications/AppMemoryTagging.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a7101324453..1e39f370ab0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5493,6 +5493,22 @@ android:value="@string/menu_key_apps"/> + + + + + + + + + + + Native debugging is always blocked for preinstalled apps + Memory tagging + Show notifications about detected errors + Memory tagging provides strong protections against exploitation of heap memory bugs (e.g. use-after-free, buffer overflow). + Memory tagging is always enabled for preinstalled apps + Memory tagging is always enabled for apps that don’t have native code + This app has opted-in to memory tagging + + Enable by default + + This setting doesn’t apply to apps that are known to be compatible with memory tagging + "Memory tagging provides strong protections against exploitation of heap memory bugs (e.g. use-after-free, buffer overflow). + +Memory tagging is always enabled for preinstalled apps and for third-party apps that are known to be +compatible with it (e.g. apps that don’t have native code), regardless of this setting." + Enabled by default + + Disabled by default, except for apps that are known to be compatible with memory tagging + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index c56c38ef64c..d2ed9ca38c0 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -29,6 +29,17 @@ android:title="@string/aep_hmalloc" settings:controller="com.android.settings.applications.HardenedMallocAppListPrefController" /> + + + + () { + + override fun getAppSwitch() = AswUseMemoryTagging.I + + override fun getAswTitle(ctx: Context) = ctx.getText(R.string.aep_memtag) + + override fun getNotificationToggleTitle(ctx: Context) = ctx.getText(R.string.aep_memtag_notif_toggle_time) + + override fun getDetailFragmentClass() = AppMemtagFragment::class +} + +private val isMemoryTaggingSupported = Zygote.nativeSupportsMemoryTagging() + +@Composable +fun AppMemtagPreference(app: ApplicationInfo) { + if (!isMemoryTaggingSupported) { + return + } + + val context = LocalContext.current + AswPreference(context, app, AswAdapterUseMemoryTagging) +} + +class AppMemtagFragment : AswExploitProtectionFragment() { + + override fun getAswAdapter() = AswAdapterUseMemoryTagging + + override fun getSummaryForImmutabilityReason(ir: Int): CharSequence? { + val id = when (ir) { + AppSwitch.IR_IS_SYSTEM_APP -> R.string.aep_memtag_dvr_is_system_app + AppSwitch.IR_NO_NATIVE_CODE -> R.string.aep_memtag_dvr_no_native_code + AppSwitch.IR_OPTED_IN_VIA_MANIFEST -> R.string.aep_memtag_dvr_manifest_opt_in + else -> return null + } + return getText(id) + } + + override fun updateFooter(fp: FooterPreference) { + fp.setTitle(R.string.aep_memtag_footer) + } +} + +class AppDefaultMemtagPrefController(ctx: Context, key: String) : + BoolSettingFragmentPrefController(ctx, key, ExtSettings.FORCE_APP_MEMTAG_BY_DEFAULT) { + + private val isSupported = Zygote.nativeSupportsMemoryTagging() + + override fun getAvailabilityStatus(): Int { + if (!isSupported) { + return UNSUPPORTED_ON_DEVICE + } + + return super.getAvailabilityStatus() + } + + override fun getSummaryOn() = resText(R.string.aep_default_memtag_summary_on) + override fun getSummaryOff() = resText(R.string.aep_default_memtag_summary_off) +} + +class AppDefaultMemtagFragment : BoolSettingFragment() { + + override fun getSetting() = ExtSettings.FORCE_APP_MEMTAG_BY_DEFAULT + + override fun getTitle() = resText(R.string.aep_memtag) + + override fun getMainSwitchTitle() = resText(R.string.aep_default_memtag_main_switch_title) + override fun getMainSwitchSummary() = resText(R.string.aep_default_memtag_main_switch_summary) + + override fun addExtraPrefs(screen: PreferenceScreen) { + AswAdapterUseMemoryTagging.addAppListPageLink(screen) + } + + override fun makeFooterPref(builder: FooterPreference.Builder): FooterPreference { + val text = AppPrefUtils.getFooterForDefaultHardeningSetting(requireContext(), + R.string.aep_default_memtag_footer) + return builder.setTitle(text).build() + } +} + +class MemtagAppListPrefController(context: Context, preferenceKey: String) : + AswAppListPrefController(context, preferenceKey, AswAdapterUseMemoryTagging) { + + private val isSupported = Zygote.nativeSupportsMemoryTagging() + + override fun getAvailabilityStatus(): Int { + if (!isSupported) { + return UNSUPPORTED_ON_DEVICE + } + return ExtSettingControllerHelper + .getSecondaryUserOnlySettingAvailability(mContext) + } +} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index c69e658103d..8ea84bc6ce2 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -219,6 +219,7 @@ public class SettingsGateway { */ public static final String[] ENTRY_FRAGMENTS = { com.android.settings.applications.AppNativeDebuggingFragment.class.getName(), + com.android.settings.applications.AppMemtagFragment.class.getName(), com.android.settings.applications.AppHardenedMallocFragment.class.getName(), com.android.settings.safetycenter.ExploitProtectionFragment.class.getName(), AdvancedConnectedDeviceDashboardFragment.class.getName(), diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index 09c762f2fc5..f7c62225702 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -103,6 +103,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { HomePageProvider, AppsMainPageProvider, com.android.settings.applications.AswAdapterUseHardenedMalloc.makeAppListPageProvider(), + com.android.settings.applications.AswAdapterUseMemoryTagging.makeAppListPageProvider(), com.android.settings.applications.AswAdapterUseExtendedVaSpace.makeAppListPageProvider(), com.android.settings.applications.AswAdapterNativeDebugging.makeAppListPageProvider(), AllAppListPageProvider, diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index cce2f233c69..a83b10e87dc 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -181,6 +181,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { Category(title = stringResource(R.string.exploit_protection_category_title)) { AppExploitProtectionCompatModeSwitchPreference(app, packageInfoPresenter) com.android.settings.applications.AppHardenedMallocPreference(app) + com.android.settings.applications.AppMemtagPreference(app) com.android.settings.applications.AppExtendedVaSpacePreference(app) com.android.settings.applications.AppNativeDebuggingPreference(app) } From bb0dee7d80f1c57efcb125d5556b0d792aeff9eb Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Mon, 27 Nov 2023 21:47:26 +0200 Subject: [PATCH 074/128] add toggle for system process crash notifications --- res/values/strings_ext.xml | 3 +++ res/xml/more_security_privacy_settings.xml | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index edd4090895a..c08c6b84daa 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -126,4 +126,7 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. "Warning: enabling this setting might cause some apps to stop working. + Notify about system process crashes + Such crashes may indicate exploitation attempts + diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml index 99a56dd9ef1..3da6e0d25f0 100644 --- a/res/xml/more_security_privacy_settings.xml +++ b/res/xml/more_security_privacy_settings.xml @@ -237,6 +237,14 @@ settings:keywords="@string/keywords_app_pinning" settings:controller="com.android.settings.security.ScreenPinningPreferenceController" /> + + Date: Fri, 29 Sep 2023 16:16:00 +0300 Subject: [PATCH 075/128] Widevine provisioning setting Signed-off-by: maade69 <70593890+maade69@users.noreply.github.com> --- res/values/strings_ext.xml | 5 +++ res/xml/network_provider_internet.xml | 6 +++ .../WidevineProvisioningPrefController.java | 43 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/com/android/settings/network/WidevineProvisioningPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index c08c6b84daa..3e47e22f60b 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -105,6 +105,11 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. Use secure app spawning Launch apps in a more secure way than Android which takes slightly longer and increases memory usage by app processes. + Widevine provisioning + GrapheneOS proxy + Google server + The provisioned certificate will be used for accessing DRM protected content. + App installs and updates diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml index b4becf7c709..594d307f83e 100644 --- a/res/xml/network_provider_internet.xml +++ b/res/xml/network_provider_internet.xml @@ -138,4 +138,10 @@ android:title="@string/remote_provisioning_title" android:order="35" settings:controller="com.android.settings.network.RemoteProvisioningPrefController" /> + + diff --git a/src/com/android/settings/network/WidevineProvisioningPrefController.java b/src/com/android/settings/network/WidevineProvisioningPrefController.java new file mode 100644 index 00000000000..09d62f012ac --- /dev/null +++ b/src/com/android/settings/network/WidevineProvisioningPrefController.java @@ -0,0 +1,43 @@ +/* + * 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.WidevineProvisioningSettings; + +import com.android.settings.R; +import androidx.preference.PreferenceScreen; +import com.android.settings.ext.IntSettingPrefController; +import com.android.settings.ext.RadioButtonPickerFragment2; + +public class WidevineProvisioningPrefController extends IntSettingPrefController { + + public WidevineProvisioningPrefController(Context ctx, String key) { + super(ctx, key, WidevineProvisioningSettings.SERVER_SETTING); + } + + @Override + public void addPrefsAfterList(RadioButtonPickerFragment2 fragment, PreferenceScreen screen) { + addFooterPreference(screen, R.string.widevine_provisioning_footer); + } + + @Override + protected void getEntries(Entries entries) { + entries.add(R.string.widevine_provisioning_enabled_grapheneos_proxy, WidevineProvisioningSettings.WV_GRAPHENEOS_PROXY); + entries.add(R.string.widevine_provisioning_enabled_google_server, WidevineProvisioningSettings.WV_STANDARD_SERVER); + } +} From 1dd434f12108f5e2efa2182dddff65145e414d36 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Mon, 18 Dec 2023 15:38:16 +0200 Subject: [PATCH 076/128] remove confusing mention of Android Auto from "Connected devices" screen --- .../AdvancedConnectedDeviceController.java | 10 +------- ...AdvancedConnectedDeviceControllerTest.java | 25 ------------------- 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceController.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceController.java index 2855f0ab16a..2f7dd6ff789 100644 --- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceController.java +++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceController.java @@ -34,7 +34,6 @@ public class AdvancedConnectedDeviceController extends BasePreferenceController private static final String DRIVING_MODE_SETTINGS_ENABLED = "gearhead:driving_mode_settings_enabled"; - private static final String GEARHEAD_PACKAGE = "com.google.android.projection.gearhead"; public AdvancedConnectedDeviceController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -59,7 +58,7 @@ public static int getConnectedDevicesSummaryResourceId(Context context) { new NfcPreferenceController(context, NfcPreferenceController.KEY_TOGGLE_NFC); return getConnectedDevicesSummaryResourceId(nfcPreferenceController, - isDrivingModeAvailable(context), isAndroidAutoSettingAvailable(context)); + isDrivingModeAvailable(context), false); } @VisibleForTesting @@ -68,13 +67,6 @@ static boolean isDrivingModeAvailable(Context context) { getInt(context.getContentResolver(), DRIVING_MODE_SETTINGS_ENABLED, 0) == 1; } - @VisibleForTesting - static boolean isAndroidAutoSettingAvailable(Context context) { - final Intent intent = new Intent(IA_SETTINGS_ACTION); - intent.setPackage(GEARHEAD_PACKAGE); - return intent.resolveActivity(context.getPackageManager()) != null; - } - @VisibleForTesting static int getConnectedDevicesSummaryResourceId(NfcPreferenceController nfcPreferenceController, diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceControllerTest.java index 6ae670dc120..2af9242dab8 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceControllerTest.java @@ -99,31 +99,6 @@ public void isDrivingModeAvailable_returnFalse() { AdvancedConnectedDeviceController.isDrivingModeAvailable(mContext)).isFalse(); } - @Test - public void isAndroidAutoSettingAvailable_returnTrue() { - final ApplicationInfo appInfo = - ApplicationInfoBuilder.newBuilder().setPackageName(ANDROID_AUTO_PACKAGE).build(); - final ActivityInfo activityInfo = new ActivityInfo(); - activityInfo.packageName = ANDROID_AUTO_PACKAGE; - activityInfo.name = ANDROID_AUTO_PACKAGE; - activityInfo.applicationInfo = appInfo; - final ResolveInfo resolveInfo = new ResolveInfo(); - resolveInfo.activityInfo = activityInfo; - mShadowPackageManager.addResolveInfoForIntent( - buildAndroidAutoSettingsIntent(), - resolveInfo); - - assertThat( - AdvancedConnectedDeviceController.isAndroidAutoSettingAvailable(mContext)).isTrue(); - } - - @Test - public void isAndroidAutoSettingAvailable_returnFalse() { - // No ResolveInfo for Android Auto, expect false. - assertThat( - AdvancedConnectedDeviceController.isAndroidAutoSettingAvailable(mContext)).isFalse(); - } - @Test public void getConnectedDevicesSummaryResourceId_NFCAndDrivingModeAvailable() { // NFC available, driving mode available From 8ba4886e7d7b82161959cc9e95f27e5e061f0763 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sat, 6 Jan 2024 19:37:47 +0200 Subject: [PATCH 077/128] add logcat viewer link to System settings --- AndroidManifest.xml | 1 + res/values/strings_ext.xml | 2 ++ res/xml/system_dashboard_fragment.xml | 7 ++++ .../system/LogcatLinkPrefController.java | 34 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 src/com/android/settings/system/LogcatLinkPrefController.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1e39f370ab0..373fd205ee2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -148,6 +148,7 @@ + Notify about system process crashes Such crashes may indicate exploitation attempts + View logs + diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml index 83cdf64fc00..60e4eaa220d 100644 --- a/res/xml/system_dashboard_fragment.xml +++ b/res/xml/system_dashboard_fragment.xml @@ -84,6 +84,13 @@ + + Date: Sat, 2 Mar 2024 11:25:46 +0200 Subject: [PATCH 078/128] add logcat viewer link to App info screen --- .../spa/app/appinfo/AppInfoSettings.kt | 1 + .../spa/app/appinfo/AppLogcatPreference.kt | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/com/android/settings/spa/app/appinfo/AppLogcatPreference.kt diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index a83b10e87dc..8162046c7df 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -160,6 +160,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { AppLocalePreference(app) AppOpenByDefaultPreference(app) DefaultAppShortcuts(app) + AppLogcatPreference(app) } Category(title = stringResource(R.string.unused_apps_category)) { diff --git a/src/com/android/settings/spa/app/appinfo/AppLogcatPreference.kt b/src/com/android/settings/spa/app/appinfo/AppLogcatPreference.kt new file mode 100644 index 00000000000..14e3c60b5e1 --- /dev/null +++ b/src/com/android/settings/spa/app/appinfo/AppLogcatPreference.kt @@ -0,0 +1,29 @@ +package com.android.settings.spa.app.appinfo + +import android.content.pm.ApplicationInfo +import android.ext.LogViewerApp +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spaprivileged.model.app.installed +import com.android.settingslib.spaprivileged.model.app.userHandle + +@Composable +fun AppLogcatPreference(app: ApplicationInfo) { + if (!app.installed) { + return + } + + val context = LocalContext.current + + Preference(object : PreferenceModel { + override val title = stringResource(R.string.view_logs) + override val onClick = { + val intent = LogViewerApp.getPackageLogcatIntent(app.packageName) + context.startActivityAsUser(intent, app.userHandle) + } + }) +} From 68e48876e43bd542bcdd6757c75d85f4b7e46837 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 21 Feb 2024 16:39:26 +0200 Subject: [PATCH 079/128] add USB-C port and pogo pins security setting --- res/values/strings_ext.xml | 32 ++++ res/xml/exploit_protection_settings.xml | 10 ++ .../UsbPortSecurityPrefController.java | 141 ++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 src/com/android/settings/security/UsbPortSecurityPrefController.java diff --git a/res/values/strings_ext.xml b/res/values/strings_ext.xml index e9ad5f1089e..60f01489f8b 100644 --- a/res/values/strings_ext.xml +++ b/res/values/strings_ext.xml @@ -136,4 +136,36 @@ There are HTTP, HTTPS, DNS, DNS-over-TLS and DNS-over-HTTPS connectivity checks. View logs + USB-C port + Off + Turns off the USB-C port. + Doesn’t impact the separate charging mode, which is activated by turning off the device and plugging it in to a charger. + + Charging-only + Turns off all non-charging USB-C port functionality. + Charging-only when locked + Enables the charging-only mode when the + device is locked and disables it when the device becomes unlocked. If the port is already + plugged-in at the time when the device becomes locked, then the activation of charging-only + mode will be delayed until the port is disconnected. + Charging-only when locked, except before first unlock + This mode has the same behavior + as the “%1$s” mode, except for an additional exemption for before-first-unlock connections. + + On + Does not add any restrictions. + + USB-C port and pogo pins + USB-C port off, pogo pins used only for charging + Turns off the USB-C port and turns off all + non-charging pogo pins functionality. Doesn’t impact the separate charging mode, which is + activated by turning off the device and plugging it in to a charger. + + Turns off all non-charging USB-C port and pogo pins functionality. + Enables the charging-only mode when the + device is locked and disables it when the device becomes unlocked. If the port is already + plugged-in at the time when the device becomes locked, then the activation of charging-only + mode will be delayed until the port is disconnected. The same behavior applies to pogo pins. + + diff --git a/res/xml/exploit_protection_settings.xml b/res/xml/exploit_protection_settings.xml index d2ed9ca38c0..530e97cd956 100644 --- a/res/xml/exploit_protection_settings.xml +++ b/res/xml/exploit_protection_settings.xml @@ -10,6 +10,16 @@ android:title="@string/auto_reboot_title" settings:controller="com.android.settings.security.AutoRebootPrefController" /> + + + + (); + usbManager.setSecurityStateForAllPorts(state, new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode != android.hardware.usb.ext.IUsbExt.NO_ERROR) { + String msg = "setPortSecurityState failed, " + + "resultCode: " + resultCode; + if (resultData != null) { + msg += ", resultData: " + resultData.toStringDeep(); + } + throw new RuntimeException(msg); + } + future.complete(null); + } + }); + try { + future.get(3, TimeUnit.SECONDS); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void setState(int prevSetting, @android.hardware.usb.ext.PortSecurityState int state) { + var usbManager = requireNonNull(mContext.getSystemService(UsbManager.class)); + if (prevSetting == UsbPortSecurity.MODE_CHARGING_ONLY && state >= UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED) { + // Turn USB ports off first to trigger reconnection of devices that were connected + // in charging-only state. Simply enabling the data path is not enough in some + // advanced scenarios, e.g. when port alt mode or port role switching are used. + setSecurityStateForAllPortsSync(usbManager, PortSecurityState.DISABLED); + } + setSecurityStateForAllPortsSync(usbManager, state); + } + + @Override + protected boolean setValue(int val) { + int prevSetting = getCurrentValue(); + boolean res = super.setValue(val); + if (!res) { + return false; + } + + int pss = switch (val) { + case UsbPortSecurity.MODE_DISABLED -> PortSecurityState.DISABLED; + case UsbPortSecurity.MODE_CHARGING_ONLY -> PortSecurityState.CHARGING_ONLY_IMMEDIATE; + case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED -> PortSecurityState.ENABLED; + case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED_AFU -> PortSecurityState.ENABLED; + case UsbPortSecurity.MODE_ENABLED -> PortSecurityState.ENABLED; + default -> throw new IllegalArgumentException(Integer.toString(val)); + }; + setState(prevSetting, pss); + return true; + } + + @Override + protected boolean isCredentialConfirmationRequired() { + return true; + } +} From ad372d10c1ff1b09d1b926781196c84eab88926d Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Thu, 7 Mar 2024 12:21:53 +0200 Subject: [PATCH 080/128] enable "About phone -> Battery information" screen Squashed with: commit 50f8f1d6d2c588e8e5504f6f9f6c071c6b26938f Author: Daniel Micay Date: Sat Mar 9 10:06:21 2024 -0500 use 2021-01-01 as minimum battery date --- .../fuelgauge/BatterySettingsFeatureProviderImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java index 4b5d9526f16..acc67ecb639 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java @@ -32,17 +32,17 @@ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatur @Override public boolean isManufactureDateAvailable(Context context, long manufactureDateMs) { - return false; + return manufactureDateMs > 1_609_459_200_000L; // 2021-01-01 } @Override public boolean isFirstUseDateAvailable(Context context, long firstUseDateMs) { - return false; + return firstUseDateMs > 1_609_459_200_000L; // 2021-01-01 } @Override public boolean isBatteryInfoEnabled(Context context) { - return false; + return true; } @Override From 46671242a154b151c1b1c5b5b6f3e01701c452a2 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Sun, 17 Mar 2024 17:40:23 +0200 Subject: [PATCH 081/128] fix footer formatting issue on App pinning screen Since 20976c3a53802a8b78cab20e49154244ddaf9ddd screen_pinning_description is a formatted string, it can't be used directly from XML. --- res/xml/screen_pinning_settings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/res/xml/screen_pinning_settings.xml b/res/xml/screen_pinning_settings.xml index 2c4402da89e..f06b42ceab1 100644 --- a/res/xml/screen_pinning_settings.xml +++ b/res/xml/screen_pinning_settings.xml @@ -30,7 +30,6 @@ From 4adf64eb1a48262e8ebbdd14173fdf2a529d9632 Mon Sep 17 00:00:00 2001 From: Jitesh Singh Date: Tue, 30 Jan 2024 19:48:12 +0530 Subject: [PATCH 082/128] fixes redundant horizontal margin issue on tablets --- res/values-sw600dp-v31/dimens.xml | 9 +++++++++ res/values-sw600dp/dimens.xml | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 res/values-sw600dp-v31/dimens.xml diff --git a/res/values-sw600dp-v31/dimens.xml b/res/values-sw600dp-v31/dimens.xml new file mode 100644 index 00000000000..3542f62dc08 --- /dev/null +++ b/res/values-sw600dp-v31/dimens.xml @@ -0,0 +1,9 @@ + + + 24dp + 24dp + 8dp + 20dp + 20dp + 8dp + diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 55735e66322..170d6998c36 100755 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -29,6 +29,14 @@ 348dp + + 24dp + 24dp + 8dp + 20dp + 20dp + 8dp + 104dp 448dp From cab9cb3e11a7203eb5a24cecfab77115c76057b5 Mon Sep 17 00:00:00 2001 From: Jitesh Singh Date: Wed, 31 Jan 2024 01:41:53 +0530 Subject: [PATCH 083/128] internet setup activity Co-authored-by: Dmitry Muhomor Co-authored-by: quh4gko8 <88831734+quh4gko8@users.noreply.github.com> --- AndroidManifest.xml | 13 ++ res/drawable/baseline_wifi.xml | 10 ++ res/drawable/baseline_wifi_glif.xml | 7 + res/layout/settings_main_prefs.xml | 62 ++++--- res/values/dimens.xml | 5 + res/values/themes_suw.xml | 2 + .../network/InternetSetupActivity.java | 52 ++++++ .../network/NetworkProviderSettings.java | 4 +- .../network/NetworkProviderSetup.java | 155 ++++++++++++++++++ 9 files changed, 276 insertions(+), 34 deletions(-) create mode 100644 res/drawable/baseline_wifi.xml create mode 100644 res/drawable/baseline_wifi_glif.xml create mode 100644 src/com/android/settings/network/InternetSetupActivity.java create mode 100644 src/com/android/settings/network/NetworkProviderSetup.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 373fd205ee2..5cb17f99642 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -516,6 +516,19 @@ android:value="true" /> + + + + + + + + + + + diff --git a/res/drawable/baseline_wifi_glif.xml b/res/drawable/baseline_wifi_glif.xml new file mode 100644 index 00000000000..3f12842b089 --- /dev/null +++ b/res/drawable/baseline_wifi_glif.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/res/layout/settings_main_prefs.xml b/res/layout/settings_main_prefs.xml index 48352e2efa0..5111332bfbe 100644 --- a/res/layout/settings_main_prefs.xml +++ b/res/layout/settings_main_prefs.xml @@ -34,40 +34,38 @@ android:layout_height="0dp" android:layout_weight="1"/> - - -