From 09a6899fc2271d260ec2979b1afc8eef1847b34a Mon Sep 17 00:00:00 2001 From: changbetty Date: Wed, 21 Dec 2022 10:16:10 +0000 Subject: [PATCH 01/12] Change the intent flag to immutable Bug: 256590210 Test: make RunSettingsRoboTests ROBOTEST_FILTER=MediaVolumePreferenceControllerTest Change-Id: I68132f863027fab19351b4fdbc363e5c274e3327 --- .../notification/MediaVolumePreferenceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index 0ddba089b7c..443f748f7d9 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -124,7 +124,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont getWorker().getActiveLocalMediaController().getPackageName()); pi = PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } else { final CachedBluetoothDevice bluetoothDevice = ((BluetoothMediaDevice) mMediaDevice).getCachedDevice(); @@ -141,7 +141,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont && getWorker().getActiveLocalMediaController() != null); pi = PendingIntent.getActivity(context, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } final IconCompat icon = getBroadcastIcon(context); From 0dcd6a5492fae67e0d7f1fb83f6d4db7eaa15b55 Mon Sep 17 00:00:00 2001 From: changbetty Date: Wed, 21 Dec 2022 10:16:10 +0000 Subject: [PATCH 02/12] [RESTRICT AUTOMERGE] Change the intent flag to immutable Bug: 256590210 Test: make RunSettingsRoboTests ROBOTEST_FILTER=MediaVolumePreferenceControllerTest Change-Id: I68132f863027fab19351b4fdbc363e5c274e3327 Merged-In: I68132f863027fab19351b4fdbc363e5c274e3327 --- .../notification/MediaVolumePreferenceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index 05c8e033930..ccde9370580 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -120,7 +120,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont getWorker().getActiveLocalMediaController().getPackageName()); pi = PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } else { final CachedBluetoothDevice bluetoothDevice = ((BluetoothMediaDevice) mMediaDevice).getCachedDevice(); @@ -135,7 +135,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont bluetoothDevice.getAddress()); pi = PendingIntent.getActivity(context, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } final IconCompat icon = getBroadcastIcon(context); From f3b323e378ee5d98875711216cbd92f4fa795fc0 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 03/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 18cb4b3b80b..54455d4f110 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -408,7 +408,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings()); mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); From 22e4eebafe1bb646618fa4b64e5038a9dc00ac14 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 04/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 18cb4b3b80b..54455d4f110 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -408,7 +408,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings()); mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); From 9191ec13e25e28fa9d6afbbb0573557c7b891520 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 05/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java old mode 100755 new mode 100644 index dbe03d9b2f8..2a705e220c0 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -385,7 +385,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( From 0616b484e963a649bcc465143c46eaaf6a078b3b Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 06/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java old mode 100755 new mode 100644 index dbe03d9b2f8..2a705e220c0 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -385,7 +385,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( From 16500a7ada6b0730bec2787055667c4394fa3468 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 07/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java old mode 100755 new mode 100644 index dbe03d9b2f8..2a705e220c0 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -385,7 +385,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( From 1a43a98334d7222b4ab1713721652c0ad7bbe8c2 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 08/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java old mode 100755 new mode 100644 index 69ddd58ae40..784908f27dd --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -373,7 +373,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( From 86914bedc84474c152e4536fb3cfa2fb488030b8 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 4 Jan 2023 09:40:38 +0000 Subject: [PATCH 09/12] Add DISALLOW_APPS_CONTROL check into uninstall app for all users Settings App info page supports a "Uninstall for all users" function when multiple users are enabled. It bypasses the restriction of DISALLOW_APPS_CONTROL which breaks the user isolation guideline. To fix this vulnerability, we should check the DISALLOW_APPS_CONTROL restriction to provide the "Uninstall for all users" function. Bug: 258653813 Test: manual & robotests Change-Id: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 Merged-In: I5d3bbcbaac439c4f7a1e6a9ade7775ff4f2f2ec6 --- .../applications/appinfo/AppInfoDashboardFragment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java old mode 100755 new mode 100644 index 69ddd58ae40..784908f27dd --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -373,7 +373,13 @@ public class AppInfoDashboardFragment extends DashboardFragment return; } super.onPrepareOptionsMenu(menu); - menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); + final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); + uninstallAllUsersItem.setVisible( + shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); + if (uninstallAllUsersItem.isVisible()) { + RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), + uninstallAllUsersItem, mAppsControlDisallowedAdmin); + } mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( From 814eff1c24b66e6e1acc5e6c25ae1a579c7aeee7 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Fri, 6 Jan 2023 15:24:44 +0800 Subject: [PATCH 10/12] Fix the inverted logic to judge whether an app is system app. Bug: 264626206 Fix: 264626206 Test: maunal Change-Id: I3750e3b11e444add27243c492cf8968373cd57d0 --- .../settings/fuelgauge/batteryusage/DataProcessor.java | 3 +-- .../settings/fuelgauge/batteryusage/DataProcessorTest.java | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index 649adbe8069..9659d3cbd8f 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -1826,8 +1826,7 @@ public final class DataProcessor { if (appEntry == null || appEntry.info == null) { return false; } - return !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp( - appEntry); + return ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(appEntry); } private static boolean shouldShowBatteryAttributionList(final Context context) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java index a1caa2c88a9..7cf80169b8d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -1660,7 +1660,8 @@ public final class DataProcessorTest { batteryHistEntry); doReturn(mAppEntry).when(mApplicationsState).getEntry(anyString(), anyInt()); mAppEntry.info = mApplicationInfo; - mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + mApplicationInfo.flags = + ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; boolean needsCombineInSystemApp = DataProcessor.needsCombineInSystemApp( batteryDiffEntry, List.of(), mApplicationsState); @@ -1690,7 +1691,7 @@ public final class DataProcessorTest { batteryHistEntry); doReturn(mAppEntry).when(mApplicationsState).getEntry(anyString(), anyInt()); mAppEntry.info = mApplicationInfo; - mApplicationInfo.flags = 0; + mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; boolean needsCombineInSystemApp = DataProcessor.needsCombineInSystemApp( batteryDiffEntry, List.of(), mApplicationsState); From 833e47d851d9d9798ce16c1e78db8b4a14cd89c6 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Thu, 5 Jan 2023 14:13:09 +0800 Subject: [PATCH 11/12] [Battery usage U] [UI] Show total "Screen On Time" in the battery usage page Screen record: https://drive.google.com/open?id=16ZOp1E2YBzWQXbnXl786FaLFPel-S9CF&authuser=0&resourcekey=0-oRqCrdTc9FZjVgsq9orhEw&usp=drive_link For Arabic: https://drive.google.com/open?id=1zh_4jcUnqLC6CDgwju1qQkWJ0QCtm19c&authuser=0&resourcekey=0-kuKfDdOTWxqOUmD0RfPNLQ&usp=drive_link Next step: show screen on time for each app Bug: 258120710 Test: manual Change-Id: I2085a2a85ebd50b2ac876972f6a8ebbf6f20246c --- res/layout/preference_text_view.xml | 25 ++++ res/values/strings.xml | 4 + res/xml/power_usage_advanced.xml | 12 ++ .../BatteryChartPreferenceController.java | 28 ++++- .../batteryusage/PowerUsageAdvanced.java | 5 + .../batteryusage/ScreenOnTimeController.java | 110 ++++++++++++++++++ .../batteryusage/TextViewPreference.java | 51 ++++++++ .../ScreenOnTimeControllerTest.java | 104 +++++++++++++++++ .../batteryusage/TextViewPreferenceTest.java | 60 ++++++++++ 9 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 res/layout/preference_text_view.xml create mode 100644 src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java create mode 100644 src/com/android/settings/fuelgauge/batteryusage/TextViewPreference.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TextViewPreferenceTest.java diff --git a/res/layout/preference_text_view.xml b/res/layout/preference_text_view.xml new file mode 100644 index 00000000000..3d0b2a1d9c7 --- /dev/null +++ b/res/layout/preference_text_view.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 1988d9dd688..bc5de68e965 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5196,6 +5196,10 @@ Battery usage since last full charge Battery usage for %s + + Screen time since last full charge + + Screen time for %s Breakdown by apps diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml index af6152abfd2..eaefe9df338 100644 --- a/res/xml/power_usage_advanced.xml +++ b/res/xml/power_usage_advanced.xml @@ -26,6 +26,18 @@ settings:controller= "com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController" /> + + + + + + > mBatteryUsageMap; + @VisibleForTesting + Map> mScreenOnTimeMap; private boolean mIs24HourFormat; private boolean mHourlyChartVisible = true; @@ -108,6 +123,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private BatteryChartViewModel mDailyViewModel; private List mHourlyViewModels; private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener; + private OnScreenOnTimeUpdatedListener mOnScreenOnTimeUpdatedListener; private final SettingsActivity mActivity; private final MetricsFeatureProvider mMetricsFeatureProvider; @@ -202,6 +218,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mOnBatteryUsageUpdatedListener = listener; } + void setOnScreenOnTimeUpdatedListener(OnScreenOnTimeUpdatedListener listener) { + mOnScreenOnTimeUpdatedListener = listener; + } + void setBatteryHistoryMap( final Map> batteryHistoryMap) { Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null" @@ -212,6 +232,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll DataProcessManager.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, batteryCallbackData -> { mBatteryUsageMap = batteryCallbackData.getBatteryUsageMap(); + mScreenOnTimeMap = batteryCallbackData.getDeviceScreenOnTime(); refreshUi(); }); Log.d(TAG, "getBatteryLevelData: " + batteryLevelData); @@ -318,7 +339,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return false; } - + if (mOnScreenOnTimeUpdatedListener != null && mScreenOnTimeMap != null + && mScreenOnTimeMap.get(mDailyChartIndex) != null) { + mOnScreenOnTimeUpdatedListener.onScreenOnTimeUpdated( + mScreenOnTimeMap.get(mDailyChartIndex).get(mHourlyChartIndex), + getSlotInformation()); + } if (mOnBatteryUsageUpdatedListener != null && mBatteryUsageMap != null && mBatteryUsageMap.get(mDailyChartIndex) != null) { final BatteryDiffData slotUsageData = diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java index c7cf70e3b97..7c4478e66c6 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java @@ -129,14 +129,18 @@ public class PowerUsageAdvanced extends PowerUsageBase { mBatteryChartPreferenceController = new BatteryChartPreferenceController( context, getSettingsLifecycle(), (SettingsActivity) getActivity()); + ScreenOnTimeController screenOnTimeController = new ScreenOnTimeController(context); BatteryUsageBreakdownController batteryUsageBreakdownController = new BatteryUsageBreakdownController( context, getSettingsLifecycle(), (SettingsActivity) getActivity(), this); + mBatteryChartPreferenceController.setOnScreenOnTimeUpdatedListener( + screenOnTimeController::handleSceenOnTimeUpdated); mBatteryChartPreferenceController.setOnBatteryUsageUpdatedListener( batteryUsageBreakdownController::handleBatteryUsageUpdated); controllers.add(mBatteryChartPreferenceController); + controllers.add(screenOnTimeController); controllers.add(batteryUsageBreakdownController); setBatteryChartPreferenceController(); return controllers; @@ -192,6 +196,7 @@ public class PowerUsageAdvanced extends PowerUsageBase { final List controllers = new ArrayList<>(); controllers.add(new BatteryChartPreferenceController( context, null /* lifecycle */, null /* activity */)); + controllers.add((new ScreenOnTimeController(context))); controllers.add(new BatteryUsageBreakdownController( context, null /* lifecycle */, null /* activity */, null /* fragment */)); diff --git a/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java new file mode 100644 index 00000000000..e78285f6093 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batteryusage; + +import android.content.Context; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.AbsoluteSizeSpan; + +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceScreen; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.utils.StringUtil; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Controller for screen on time in battery usage page. */ +public class ScreenOnTimeController extends BasePreferenceController { + private static final String TAG = "ScreenOnTimeController"; + private static final String ROOT_PREFERENCE_KEY = "screen_on_time_category"; + private static final String SCREEN_ON_TIME_TEXT_PREFERENCE_KEY = "screen_on_time_text"; + private static final Pattern NUMBER_PATTERN = Pattern.compile("[\\d]*[\\.,]?[\\d]+"); + + @VisibleForTesting + Context mPrefContext; + @VisibleForTesting + PreferenceCategory mRootPreference; + @VisibleForTesting + TextViewPreference mScreenOnTimeTextPreference; + + public ScreenOnTimeController(Context context) { + super(context, ROOT_PREFERENCE_KEY); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPrefContext = screen.getContext(); + mRootPreference = screen.findPreference(ROOT_PREFERENCE_KEY); + mScreenOnTimeTextPreference = screen.findPreference(SCREEN_ON_TIME_TEXT_PREFERENCE_KEY); + } + + void handleSceenOnTimeUpdated(Long screenOnTime, String slotTimestamp) { + if (screenOnTime == null) { + mRootPreference.setVisible(false); + mScreenOnTimeTextPreference.setVisible(false); + return; + } + showCategoryTitle(slotTimestamp); + showScreenOnTimeText(screenOnTime); + } + + @VisibleForTesting + void showCategoryTitle(String slotTimestamp) { + mRootPreference.setTitle(slotTimestamp == null + ? mPrefContext.getString( + R.string.screen_time_category_last_full_charge) + : mPrefContext.getString( + R.string.screen_time_category_for_slot, slotTimestamp)); + mRootPreference.setVisible(true); + } + + @VisibleForTesting + void showScreenOnTimeText(Long screenOnTime) { + final CharSequence timeSequence = + StringUtil.formatElapsedTime(mPrefContext, (double) screenOnTime, + /*withSeconds=*/ false, /*collapseTimeUnit=*/ false); + mScreenOnTimeTextPreference.setText(enlargeFontOfNumber(timeSequence)); + mScreenOnTimeTextPreference.setVisible(true); + } + + @VisibleForTesting + static CharSequence enlargeFontOfNumber(CharSequence text) { + if (TextUtils.isEmpty(text)) { + return ""; + } + + final SpannableString spannableText = new SpannableString(text); + final Matcher matcher = NUMBER_PATTERN.matcher(text); + while (matcher.find()) { + spannableText.setSpan(new AbsoluteSizeSpan(36, true /* dip */), matcher.start(), + matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return spannableText; + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/TextViewPreference.java b/src/com/android/settings/fuelgauge/batteryusage/TextViewPreference.java new file mode 100644 index 00000000000..22faabb3af0 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/TextViewPreference.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batteryusage; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; + +/** A preference for a single text view. */ +public class TextViewPreference extends Preference { + private static final String TAG = "TextViewPreference"; + + @VisibleForTesting + CharSequence mText; + + public TextViewPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.preference_text_view); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + final TextView textView = (TextView) view.findViewById(R.id.text); + textView.setText(mText); + } + + void setText(CharSequence text) { + mText = text; + notifyChanged(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeControllerTest.java new file mode 100644 index 00000000000..0e15dcde0c8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeControllerTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batteryusage; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.res.Resources; +import android.os.LocaleList; + +import androidx.preference.PreferenceCategory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.Locale; +import java.util.TimeZone; + +@RunWith(RobolectricTestRunner.class) +public final class ScreenOnTimeControllerTest { + + private Context mContext; + private ScreenOnTimeController mScreenOnTimeController; + + @Mock + private PreferenceCategory mRootPreference; + @Mock + private TextViewPreference mScreenOnTimeTextPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + Locale.setDefault(new Locale("en_US")); + org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + mContext = spy(RuntimeEnvironment.application); + final Resources resources = spy(mContext.getResources()); + resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US"))); + doReturn(resources).when(mContext).getResources(); + mScreenOnTimeController = new ScreenOnTimeController(mContext); + mScreenOnTimeController.mPrefContext = mContext; + mScreenOnTimeController.mRootPreference = mRootPreference; + mScreenOnTimeController.mScreenOnTimeTextPreference = mScreenOnTimeTextPreference; + } + + @Test + public void handleSceenOnTimeUpdated_nullScreenOnTime_hideAllPreference() { + mScreenOnTimeController.handleSceenOnTimeUpdated( + /* screenOnTime= */ null, "Friday 12:00-now"); + + verify(mRootPreference).setVisible(false); + verify(mScreenOnTimeTextPreference).setVisible(false); + } + + @Test + public void showCategoryTitle_null_sinceLastFullCharge() { + mScreenOnTimeController.showCategoryTitle(null); + + verify(mRootPreference).setTitle("Screen time since last full charge"); + verify(mRootPreference).setVisible(true); + } + + @Test + public void showCategoryTitle_notNull_slotTimestamp() { + mScreenOnTimeController.showCategoryTitle("Friday 12:00-now"); + + verify(mRootPreference).setTitle("Screen time for Friday 12:00-now"); + verify(mRootPreference).setVisible(true); + } + + @Test + public void showScreenOnTimeText_returnExpectedResult() { + mScreenOnTimeController.showScreenOnTimeText(1600000000L); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CharSequence.class); + verify(mScreenOnTimeTextPreference).setText(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().toString()).isEqualTo("18 days, 12 hr, 27 min"); + verify(mScreenOnTimeTextPreference).setVisible(true); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TextViewPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TextViewPreferenceTest.java new file mode 100644 index 00000000000..ca7e9dd2879 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TextViewPreferenceTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batteryusage; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.Context; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public final class TextViewPreferenceTest { + + private Context mContext; + private TextViewPreference mTextViewPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mTextViewPreference = new TextViewPreference(mContext, /*attrs=*/ null); + } + + @Test + public void constructor_returnExpectedResult() { + assertThat(mTextViewPreference.getLayoutResource()).isEqualTo( + R.layout.preference_text_view); + } + + @Test + public void setText_returnExpectedResult() { + final String text = "TEST_TEXT"; + mTextViewPreference.setText(text); + + assertThat(mTextViewPreference.mText.toString()).isEqualTo(text); + } +} From 86b80b2508481a9f2bd4aa4de0b0feec92526546 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Fri, 6 Jan 2023 14:29:30 +0000 Subject: [PATCH 12/12] [Settings] Reduce CDMA API access when not required Avoid from accessing CDMA roaming API to reduce some latency on UI. Bug: 264656126 Test: local Change-Id: I5c74636d8418a379724676e1f854c8adff5d4387 --- .../CdmaSystemSelectPreferenceController.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/network/telephony/cdma/CdmaSystemSelectPreferenceController.java b/src/com/android/settings/network/telephony/cdma/CdmaSystemSelectPreferenceController.java index 1b9103f93e2..4f4c35d62c9 100644 --- a/src/com/android/settings/network/telephony/cdma/CdmaSystemSelectPreferenceController.java +++ b/src/com/android/settings/network/telephony/cdma/CdmaSystemSelectPreferenceController.java @@ -23,6 +23,7 @@ import static com.android.settings.network.telephony.TelephonyConstants.Telephon import android.content.Context; import android.provider.Settings; import android.telephony.TelephonyManager; +import android.util.Log; import androidx.preference.ListPreference; import androidx.preference.Preference; @@ -34,6 +35,7 @@ import com.android.settings.network.telephony.MobileNetworkUtils; */ public class CdmaSystemSelectPreferenceController extends CdmaBasePreferenceController implements ListPreference.OnPreferenceChangeListener { + private static final String TAG = "CdmaSystemSelectPreferenceController"; public CdmaSystemSelectPreferenceController(Context context, String key) { super(context, key); @@ -43,28 +45,36 @@ public class CdmaSystemSelectPreferenceController extends CdmaBasePreferenceCont public void updateState(Preference preference) { super.updateState(preference); final ListPreference listPreference = (ListPreference) preference; - listPreference.setVisible(getAvailabilityStatus() == AVAILABLE); - boolean hasTelephonyMgr = mTelephonyManager != null; - final int mode = - hasTelephonyMgr ? mTelephonyManager.getCdmaRoamingMode() - : TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT; - if (mode != TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT) { - if (mode == TelephonyManager.CDMA_ROAMING_MODE_HOME - || mode == TelephonyManager.CDMA_ROAMING_MODE_ANY) { - listPreference.setValue(Integer.toString(mode)); - } else { - resetCdmaRoamingModeToDefault(); - } + boolean isVisible = (getAvailabilityStatus() == AVAILABLE); + listPreference.setVisible(isVisible); + if (!isVisible) { + return; } + boolean hasTelephonyMgr = mTelephonyManager != null; + try { + final int mode = + hasTelephonyMgr ? mTelephonyManager.getCdmaRoamingMode() + : TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT; + if (mode != TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT) { + if (mode == TelephonyManager.CDMA_ROAMING_MODE_HOME + || mode == TelephonyManager.CDMA_ROAMING_MODE_ANY) { + listPreference.setValue(Integer.toString(mode)); + } else { + resetCdmaRoamingModeToDefault(); + } + } - final int settingsNetworkMode = - hasTelephonyMgr ? MobileNetworkUtils.getNetworkTypeFromRaf( - (int) mTelephonyManager.getAllowedNetworkTypesForReason( - TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)) - : NETWORK_MODE_UNKNOWN; - final boolean enableList = settingsNetworkMode != NETWORK_MODE_LTE_GSM_WCDMA - && settingsNetworkMode != NETWORK_MODE_NR_LTE_GSM_WCDMA; - listPreference.setEnabled(enableList); + final int settingsNetworkMode = + hasTelephonyMgr ? MobileNetworkUtils.getNetworkTypeFromRaf( + (int) mTelephonyManager.getAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)) + : NETWORK_MODE_UNKNOWN; + final boolean enableList = settingsNetworkMode != NETWORK_MODE_LTE_GSM_WCDMA + && settingsNetworkMode != NETWORK_MODE_NR_LTE_GSM_WCDMA; + listPreference.setEnabled(enableList); + } catch (Exception exception) { + Log.e(TAG, "Fail to access framework API", exception); + } } @Override