From cf1190c7cb7c577dfdb6a20a9fb74990dc29fa43 Mon Sep 17 00:00:00 2001 From: Wesley Wang Date: Wed, 19 Apr 2023 20:00:31 +0800 Subject: [PATCH 01/18] Update battery health state intent (2/3) - Update the extra from overheated to longlife Bug: 278192441 Test: make SettingsRoboTests Change-Id: I5a906e64effbd9aaf84446c0670249afefdf7da8 --- .../fuelgauge/BatteryBroadcastReceiver.java | 31 ++++++++++++------- .../settings/fuelgauge/BatteryInfo.java | 10 +++--- .../settings/fuelgauge/BatteryUtils.java | 9 +++--- .../detectors/BatteryDefenderDetector.java | 15 +++++---- .../BatteryBroadcastReceiverTest.java | 20 ++++++++++-- ...BatteryHeaderPreferenceControllerTest.java | 4 +-- .../settings/fuelgauge/BatteryInfoTest.java | 19 ++++++------ .../settings/fuelgauge/BatteryUtilsTest.java | 16 +++++----- .../BatteryDefenderDetectorTest.java | 16 +++++----- .../detectors/DockDefenderDetectorTest.java | 8 ++--- 10 files changed, 85 insertions(+), 63 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java index 81a15ca7275..79ecd4076ae 100644 --- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java @@ -34,24 +34,20 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Use this broadcastReceiver to listen to the battery change, and it will invoke - * {@link OnBatteryChangedListener} if any of the following has been changed: - * - * 1. Battery level(e.g. 100%->99%) - * 2. Battery status(e.g. plugged->unplugged) - * 3. Battery saver(e.g. off->on) - * 4. Battery health(e.g. good->overheat) + * Use this broadcastReceiver to listen to the battery change and it will invoke + * {@link OnBatteryChangedListener} */ public class BatteryBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "BatteryBroadcastRcvr"; /** - * Callback when the following has been changed: + * Callback if any of the monitored fields has been changed: * * Battery level(e.g. 100%->99%) * Battery status(e.g. plugged->unplugged) * Battery saver(e.g. off->on) * Battery health(e.g. good->overheat) + * Battery charging status(e.g. default->long life) */ public interface OnBatteryChangedListener { void onBatteryChanged(@BatteryUpdateType int type); @@ -63,6 +59,7 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { BatteryUpdateType.BATTERY_SAVER, BatteryUpdateType.BATTERY_STATUS, BatteryUpdateType.BATTERY_HEALTH, + BatteryUpdateType.CHARGING_STATUS, BatteryUpdateType.BATTERY_NOT_PRESENT}) public @interface BatteryUpdateType { int MANUAL = 0; @@ -70,7 +67,8 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { int BATTERY_SAVER = 2; int BATTERY_STATUS = 3; int BATTERY_HEALTH = 4; - int BATTERY_NOT_PRESENT = 5; + int CHARGING_STATUS = 5; + int BATTERY_NOT_PRESENT = 6; } @VisibleForTesting @@ -78,6 +76,8 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { @VisibleForTesting String mBatteryStatus; @VisibleForTesting + int mChargingStatus; + @VisibleForTesting int mBatteryHealth; private OnBatteryChangedListener mBatteryListener; private Context mContext; @@ -121,21 +121,27 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { final String batteryLevel = Utils.getBatteryPercentage(intent); final String batteryStatus = Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false); + final int chargingStatus = intent.getIntExtra( + BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT); final int batteryHealth = intent.getIntExtra( BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN); Log.d( TAG, - "Battery changed: level=" + "Battery changed: level: " + batteryLevel - + ", status=" + + "| status: " + batteryStatus - + ", health=" + + "| chargingStatus: " + + chargingStatus + + "| health: " + batteryHealth); if (!Utils.isBatteryPresent(intent)) { Log.w(TAG, "Problem reading the battery meter."); mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_NOT_PRESENT); } else if (forceUpdate) { mBatteryListener.onBatteryChanged(BatteryUpdateType.MANUAL); + } else if (chargingStatus != mChargingStatus) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.CHARGING_STATUS); } else if (batteryHealth != mBatteryHealth) { mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH); } else if(!batteryLevel.equals(mBatteryLevel)) { @@ -145,6 +151,7 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { } mBatteryLevel = batteryLevel; mBatteryStatus = batteryStatus; + mChargingStatus = chargingStatus; mBatteryHealth = batteryHealth; } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER); diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java index d164e931a21..e7eb947848f 100644 --- a/src/com/android/settings/fuelgauge/BatteryInfo.java +++ b/src/com/android/settings/fuelgauge/BatteryInfo.java @@ -51,7 +51,7 @@ public class BatteryInfo { public int batteryStatus; public int pluggedStatus; public boolean discharging = true; - public boolean isOverheated; + public boolean isBatteryDefender; public long remainingTimeUs = 0; public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN; public String batteryPercentString; @@ -257,9 +257,9 @@ public class BatteryInfo { info.pluggedStatus = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); info.mCharging = info.pluggedStatus != 0; info.averageTimeToDischarge = estimate.getAverageDischargeTime(); - info.isOverheated = batteryBroadcast.getIntExtra( - BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN) - == BatteryManager.BATTERY_HEALTH_OVERHEAT; + info.isBatteryDefender = batteryBroadcast.getIntExtra( + BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT) + == BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE; info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus); info.batteryStatus = batteryBroadcast.getIntExtra( @@ -283,7 +283,7 @@ public class BatteryInfo { info.discharging = false; info.suggestionLabel = null; int dockDefenderMode = BatteryUtils.getCurrentDockDefenderMode(context, info); - if ((info.isOverheated && status != BatteryManager.BATTERY_STATUS_FULL + if ((info.isBatteryDefender && status != BatteryManager.BATTERY_STATUS_FULL && dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) || dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) { // Battery defender active, battery charging paused diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index 72d84ef6663..12760b18b0a 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -18,7 +18,6 @@ package com.android.settings.fuelgauge; import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.InstallSourceInfo; import android.content.pm.PackageInfo; @@ -322,10 +321,10 @@ public class BatteryUtils { } /** - * Return {@code true} if battery is overheated and charging. + * Return {@code true} if battery defender is on and charging. */ public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) { - return batteryInfo.isOverheated && !batteryInfo.discharging; + return batteryInfo.isBatteryDefender && !batteryInfo.discharging; } /** @@ -627,11 +626,11 @@ public class BatteryUtils { if (Settings.Global.getInt(context.getContentResolver(), SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 0) == 1) { return DockDefenderMode.TEMPORARILY_BYPASSED; - } else if (batteryInfo.isOverheated && FeatureFactory.getFactory(context) + } else if (batteryInfo.isBatteryDefender && FeatureFactory.getFactory(context) .getPowerUsageFeatureProvider(context) .isExtraDefend()) { return DockDefenderMode.ACTIVE; - } else if (!batteryInfo.isOverheated) { + } else if (!batteryInfo.isBatteryDefender) { return DockDefenderMode.FUTURE_BYPASS; } } diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java index 2dc057ea7a3..8b7d4c18856 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java @@ -37,13 +37,12 @@ public class BatteryDefenderDetector implements BatteryTipDetector { @Override public BatteryTip detect() { - final boolean isBasicBatteryDefend = mBatteryInfo.isOverheated - && !FeatureFactory.getFactory(mContext) - .getPowerUsageFeatureProvider(mContext) - .isExtraDefend(); - final int state = isBasicBatteryDefend - ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; - final boolean isPluggedIn = mBatteryInfo.pluggedStatus != 0; - return new BatteryDefenderTip(state, isPluggedIn); + final boolean isBasicBatteryDefend = mBatteryInfo.isBatteryDefender + && !FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext) + .isExtraDefend(); + final int state = isBasicBatteryDefend + ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; + final boolean isPluggedIn = mBatteryInfo.pluggedStatus != 0; + return new BatteryDefenderTip(state, isPluggedIn); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java index a829c40e9a6..62f812d299a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java @@ -69,6 +69,7 @@ public class BatteryBroadcastReceiverTest { mBatteryBroadcastReceiver.mBatteryLevel = BATTERY_INIT_LEVEL; mBatteryBroadcastReceiver.mBatteryStatus = BATTERY_INIT_STATUS; mBatteryBroadcastReceiver.mBatteryHealth = BatteryManager.BATTERY_HEALTH_UNKNOWN; + mBatteryBroadcastReceiver.mChargingStatus = BatteryManager.CHARGING_POLICY_DEFAULT; mBatteryBroadcastReceiver.setBatteryChangedListener(mBatteryListener); mChargingIntent = new Intent(Intent.ACTION_BATTERY_CHANGED); @@ -91,8 +92,8 @@ public class BatteryBroadcastReceiverTest { @Test public void onReceive_batteryHealthChanged_dataUpdated() { - mChargingIntent - .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT); + mChargingIntent.putExtra( + BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT); mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent); assertThat(mBatteryBroadcastReceiver.mBatteryHealth) @@ -100,6 +101,17 @@ public class BatteryBroadcastReceiverTest { verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH); } + @Test + public void onReceive_chargingStatusChanged_dataUpdated() { + mChargingIntent.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, + BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE); + mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent); + + assertThat(mBatteryBroadcastReceiver.mChargingStatus) + .isEqualTo(BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE); + verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.CHARGING_STATUS); + } + @Test public void onReceive_batteryNotPresent_shouldShowHelpMessage() { mChargingIntent.putExtra(BatteryManager.EXTRA_PRESENT, false); @@ -131,6 +143,8 @@ public class BatteryBroadcastReceiverTest { assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(batteryStatus); assertThat(mBatteryBroadcastReceiver.mBatteryHealth) .isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN); + assertThat(mBatteryBroadcastReceiver.mChargingStatus) + .isEqualTo(BatteryManager.CHARGING_POLICY_DEFAULT); verify(mBatteryListener, never()).onBatteryChanged(anyInt()); } @@ -163,6 +177,8 @@ public class BatteryBroadcastReceiverTest { Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false)); assertThat(mBatteryBroadcastReceiver.mBatteryHealth) .isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN); + assertThat(mBatteryBroadcastReceiver.mChargingStatus) + .isEqualTo(BatteryManager.CHARGING_POLICY_DEFAULT); // 2 times because register will force update the battery verify(mBatteryListener, times(2)).onBatteryChanged(BatteryUpdateType.MANUAL); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java index f94e5bf7b66..2779e0ac0c7 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java @@ -290,8 +290,8 @@ public class BatteryHeaderPreferenceControllerTest { } @Test - public void updatePreference_isOverheat_showEmptyText() { - mBatteryInfo.isOverheated = true; + public void updatePreference_isBatteryDefender_showEmptyText() { + mBatteryInfo.isBatteryDefender = true; mController.updateHeaderPreference(mBatteryInfo); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java index eb4b598823f..09eb4d0809d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java @@ -254,18 +254,18 @@ public class BatteryInfoTest { } @Test - public void testGetBatteryInfo_chargingWithOverheated_updateChargeLabel() { + public void testGetBatteryInfo_chargingWithDefender_updateChargeLabel() { doReturn(TEST_CHARGE_TIME_REMAINING) .when(mBatteryUsageStats) .getChargeTimeRemainingMs(); - mChargingBatteryBroadcast - .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT); + mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, + BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE); BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast, mBatteryUsageStats, MOCK_ESTIMATE, SystemClock.elapsedRealtime() * 1000, false /* shortString */); - assertThat(info.isOverheated).isTrue(); + assertThat(info.isBatteryDefender).isTrue(); assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_PAUSED); } @@ -278,7 +278,8 @@ public class BatteryInfoTest { 50 /* level */, 100 /* scale */, BatteryManager.BATTERY_STATUS_CHARGING) - .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT); + .putExtra(BatteryManager.EXTRA_CHARGING_STATUS, + BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE); BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, intent, mBatteryUsageStats, MOCK_ESTIMATE, SystemClock.elapsedRealtime() * 1000, @@ -290,8 +291,8 @@ public class BatteryInfoTest { @Test public void testGetBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() { doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs(); - mChargingBatteryBroadcast - .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_GOOD); + mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, + BatteryManager.CHARGING_POLICY_DEFAULT); Settings.Global.putInt(mContext.getContentResolver(), BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 1); @@ -309,8 +310,8 @@ public class BatteryInfoTest { @Test public void testGetBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() { doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); - mChargingBatteryBroadcast - .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_GOOD); + mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_CHARGING_STATUS, + BatteryManager.CHARGING_POLICY_DEFAULT); BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, BatteryTestUtils.getCustomBatteryIntent(BatteryManager.BATTERY_PLUGGED_DOCK, diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index b5f81499ea5..2fe0cec63d8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -487,32 +487,32 @@ public class BatteryUtilsTest { } @Test - public void testIsBatteryDefenderOn_isOverheatedAndIsCharging_returnTrue() { - mBatteryInfo.isOverheated = true; + public void testIsBatteryDefenderOn_isDefenderAndIsCharging_returnTrue() { + mBatteryInfo.isBatteryDefender = true; mBatteryInfo.discharging = false; assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isTrue(); } @Test - public void testIsBatteryDefenderOn_isOverheatedAndDischarging_returnFalse() { - mBatteryInfo.isOverheated = true; + public void testIsBatteryDefenderOn_isDefenderAndDischarging_returnFalse() { + mBatteryInfo.isBatteryDefender = true; mBatteryInfo.discharging = true; assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse(); } @Test - public void testIsBatteryDefenderOn_notOverheatedAndDischarging_returnFalse() { - mBatteryInfo.isOverheated = false; + public void testIsBatteryDefenderOn_notDefenderAndDischarging_returnFalse() { + mBatteryInfo.isBatteryDefender = false; mBatteryInfo.discharging = true; assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse(); } @Test - public void testIsBatteryDefenderOn_notOverheatedAndIsCharging_returnFalse() { - mBatteryInfo.isOverheated = false; + public void testIsBatteryDefenderOn_notDefenderAndIsCharging_returnFalse() { + mBatteryInfo.isBatteryDefender = false; mBatteryInfo.discharging = false; assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java index f81a4be8d63..64d5d0460ef 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java @@ -60,32 +60,32 @@ public class BatteryDefenderDetectorTest { } @Test - public void testDetect_notOverheatedNotExtraDefend_tipInvisible() { - mBatteryInfo.isOverheated = false; + public void testDetect_notDefenderNotExtraDefend_tipInvisible() { + mBatteryInfo.isBatteryDefender = false; when(mFakeFeatureFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(false); assertThat(mBatteryDefenderDetector.detect().isVisible()).isFalse(); } @Test - public void testDetect_notOverheatedIsExtraDefend_tipInvisible() { - mBatteryInfo.isOverheated = false; + public void testDetect_notDefenderIsExtraDefend_tipInvisible() { + mBatteryInfo.isBatteryDefender = false; when(mFakeFeatureFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(true); assertThat(mBatteryDefenderDetector.detect().isVisible()).isFalse(); } @Test - public void testDetect_isOverheatedIsExtraDefend_tipInvisible() { - mBatteryInfo.isOverheated = false; + public void testDetect_isDefenderIsExtraDefend_tipInvisible() { + mBatteryInfo.isBatteryDefender = false; when(mFakeFeatureFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(true); assertThat(mBatteryDefenderDetector.detect().isVisible()).isFalse(); } @Test - public void testDetect_isOverheatedNotExtraDefend_tipNew() { - mBatteryInfo.isOverheated = true; + public void testDetect_isDefenderNotExtraDefend_tipNew() { + mBatteryInfo.isBatteryDefender = true; when(mFakeFeatureFactory.powerUsageFeatureProvider.isExtraDefend()).thenReturn(false); assertThat(mBatteryDefenderDetector.detect().getState()) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/DockDefenderDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/DockDefenderDetectorTest.java index 9652a00ca18..bd2c5d1173b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/DockDefenderDetectorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/DockDefenderDetectorTest.java @@ -83,7 +83,7 @@ public class DockDefenderDetectorTest { @Test public void testDetect_dockDefenderActive() { - mBatteryInfo.isOverheated = true; + mBatteryInfo.isBatteryDefender = true; doReturn(true).when(mFakeFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); BatteryTip batteryTip = mDockDefenderDetector.detect(); @@ -95,7 +95,7 @@ public class DockDefenderDetectorTest { @Test public void testDetect_dockDefenderFutureBypass() { - mBatteryInfo.isOverheated = false; + mBatteryInfo.isBatteryDefender = false; doReturn(false).when(mFakeFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); BatteryTip batteryTip = mDockDefenderDetector.detect(); @@ -107,7 +107,7 @@ public class DockDefenderDetectorTest { @Test public void testDetect_overheatedTrue_dockDefenderDisabled() { - mBatteryInfo.isOverheated = true; + mBatteryInfo.isBatteryDefender = true; doReturn(false).when(mFakeFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); BatteryTip batteryTip = mDockDefenderDetector.detect(); @@ -131,7 +131,7 @@ public class DockDefenderDetectorTest { @Test public void testDetect_overheatedTrueAndDockDefenderNotTriggered_dockDefenderDisabled() { doReturn(false).when(mFakeFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); - mBatteryInfo.isOverheated = true; + mBatteryInfo.isBatteryDefender = true; BatteryTip batteryTip = mDockDefenderDetector.detect(); From d126303343e1b57be73844fd4c273d54a5a6f7ca Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Tue, 9 May 2023 15:17:38 +0800 Subject: [PATCH 02/18] Enable presubmit biometrics unit tests Enable biometrics/biometrics2/password unit tests Bug: 281627891 Test: atest :presubmit in Settings folder Change-Id: I2587b50b75a161c7b19f1e3430e41b98cfe6cf63 Merged-In: I2587b50b75a161c7b19f1e3430e41b98cfe6cf63 (cherry picked from commit a90536b632aa1be9e050fd2f73c36d949c70cec4) --- TEST_MAPPING | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/TEST_MAPPING b/TEST_MAPPING index 836806cea11..4e85cb8f5b0 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -2,6 +2,20 @@ "presubmit": [ { "name": "SettingsSpaUnitTests" + }, + { + "name": "SettingsUnitTests", + "options": [ + { + "include-filter": "com.android.settings.password" + }, + { + "include-filter": "com.android.settings.biometrics" + }, + { + "include-filter": "com.android.settings.biometrics2" + } + ] } ], "postsubmit": [ From 5b42447d299f9c61229b5039bd5c1974e03dc3c9 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Fri, 12 May 2023 16:35:15 +0100 Subject: [PATCH 03/18] work challenge: allow backgroud biometric auth We observed a race condition that when work challenge is launched from notification, the biometric prompt quickly dismisses itself because it detects the launcher is now the foreground app. This change attempts to workaround the issue by enabling the setAllowBackgroundAuthentication optin in BiometricPrompt so it no longer dismisses itself even if the foreground app is different. Bug: 279766640 Test: manual Change-Id: I453b7d603c6eb65f329afb38d8a190e21a7e4c01 --- src/com/android/settings/password/BiometricFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java index d364c71b331..4ad04a1a903 100644 --- a/src/com/android/settings/password/BiometricFragment.java +++ b/src/com/android/settings/password/BiometricFragment.java @@ -141,6 +141,7 @@ public class BiometricFragment extends InstrumentedFragment { .setDisallowBiometricsIfPolicyExists( promptInfo.isDisallowBiometricsIfPolicyExists()) .setReceiveSystemEvents(true) + .setAllowBackgroundAuthentication(true) .build(); } From abfb8a5e9da28932be4847727941b81e16983181 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Thu, 11 May 2023 13:41:12 +0800 Subject: [PATCH 04/18] Fix FaceUpdater.enroll unit test fail in aosp Ignore related tests Bug: 281627891 Test: atest FaceUpdaterTest Change-Id: I028a7dd7fd2e56889ac27051678ca954c896da20 --- .../com/android/settings/biometrics/face/FaceUpdaterTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java index a49b4a6c170..66514ac5068 100644 --- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java +++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java @@ -39,6 +39,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -152,6 +153,7 @@ public class FaceUpdaterTest { verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); } + @Ignore("b/282413778") @Test public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() { ArgumentCaptor callbackCaptor = @@ -180,6 +182,7 @@ public class FaceUpdaterTest { .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); } + @Ignore("b/282413778") @Test public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor callbackCaptor = @@ -201,6 +204,7 @@ public class FaceUpdaterTest { verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } + @Ignore("b/282413778") @Test public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { ArgumentCaptor callbackCaptor = From 0cd24adec55d756404e16cf8e73a26b98e4f9fa7 Mon Sep 17 00:00:00 2001 From: ykhung Date: Mon, 15 May 2023 11:01:16 +0800 Subject: [PATCH 05/18] Add allowlist mechanism for battery optimization mode Add a mechanism to add package name into the allowlist to avoid users change the battery optimization modes for specific apps in the list https://screenshot.googleplex.com/8hrHCcTh5bNYXqp Bug: 281566984 Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge.* Change-Id: I8efa6a55646d761f5bee3667a59b38ab68c74bc1 --- protos/fuelgauge_log.proto | 1 + res/values/config.xml | 4 + .../fuelgauge/BatteryBackupHelper.java | 45 ++++--- .../fuelgauge/BatteryOptimizeUtils.java | 8 ++ .../BatterySettingsMigrateChecker.java | 41 ++++++- .../BatterySettingsMigrateCheckerTest.java | 113 +++++++++++++++++- 6 files changed, 193 insertions(+), 19 deletions(-) diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto index 8512cb82506..150c2e2ce6a 100644 --- a/protos/fuelgauge_log.proto +++ b/protos/fuelgauge_log.proto @@ -20,6 +20,7 @@ message BatteryOptimizeHistoricalLogEntry { RESET = 3; RESTORE = 4; BACKUP = 5; + FORCE_RESET = 6; } optional string package_name = 1; diff --git a/res/values/config.xml b/res/values/config.xml index 52d7183ebff..334d4e574c5 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -526,6 +526,10 @@ content://com.android.settings.slices/intent/media_output_indicator + + + + diff --git a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java index 79df57ab08d..1bb3b4de001 100644 --- a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java +++ b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java @@ -91,11 +91,12 @@ public final class BatteryBackupHelper implements BackupHelper { @Override public void restoreEntity(BackupDataInputStream data) { - BatterySettingsMigrateChecker.verifyConfiguration(mContext); + BatterySettingsMigrateChecker.verifySaverConfiguration(mContext); if (!isOwner() || data == null || data.size() == 0) { Log.w(TAG, "ignore restoreEntity() for non-owner or empty data"); return; } + if (KEY_OPTIMIZATION_LIST.equals(data.getKey())) { final int dataSize = data.size(); final byte[] dataBytes = new byte[dataSize]; @@ -105,7 +106,10 @@ public final class BatteryBackupHelper implements BackupHelper { Log.e(TAG, "failed to load BackupDataInputStream", e); return; } - restoreOptimizationMode(dataBytes); + final int restoreCount = restoreOptimizationMode(dataBytes); + if (restoreCount > 0) { + BatterySettingsMigrateChecker.verifyOptimizationModes(mContext); + } } } @@ -175,17 +179,17 @@ public final class BatteryBackupHelper implements BackupHelper { } @VisibleForTesting - void restoreOptimizationMode(byte[] dataBytes) { + int restoreOptimizationMode(byte[] dataBytes) { final long timestamp = System.currentTimeMillis(); final String dataContent = new String(dataBytes, StandardCharsets.UTF_8); if (dataContent == null || dataContent.isEmpty()) { Log.w(TAG, "no data found in the restoreOptimizationMode()"); - return; + return 0; } final String[] appConfigurations = dataContent.split(BatteryBackupHelper.DELIMITER); if (appConfigurations == null || appConfigurations.length == 0) { Log.w(TAG, "no data found from the split() processing"); - return; + return 0; } int restoreCount = 0; for (int index = 0; index < appConfigurations.length; index++) { @@ -217,6 +221,7 @@ public final class BatteryBackupHelper implements BackupHelper { } Log.d(TAG, String.format("restoreOptimizationMode() count=%d in %d/ms", restoreCount, (System.currentTimeMillis() - timestamp))); + return restoreCount; } /** Dump the app optimization mode backup history data. */ @@ -225,6 +230,23 @@ public final class BatteryBackupHelper implements BackupHelper { getSharedPreferences(context), writer); } + static boolean isOwner() { + return UserHandle.myUserId() == UserHandle.USER_SYSTEM; + } + + static BatteryOptimizeUtils newBatteryOptimizeUtils( + Context context, String packageName, BatteryOptimizeUtils testOptimizeUtils) { + final int uid = BatteryUtils.getInstance(context).getPackageUid(packageName); + if (uid == BatteryUtils.UID_NULL) { + return null; + } + final BatteryOptimizeUtils batteryOptimizeUtils = + testOptimizeUtils != null + ? testOptimizeUtils /*testing only*/ + : new BatteryOptimizeUtils(context, uid, packageName); + return batteryOptimizeUtils; + } + @VisibleForTesting static SharedPreferences getSharedPreferences(Context context) { return context.getSharedPreferences( @@ -233,14 +255,11 @@ public final class BatteryBackupHelper implements BackupHelper { private void restoreOptimizationMode( String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) { - final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName); - if (uid == BatteryUtils.UID_NULL) { + final BatteryOptimizeUtils batteryOptimizeUtils = + newBatteryOptimizeUtils(mContext, packageName, mBatteryOptimizeUtils); + if (batteryOptimizeUtils == null) { return; } - final BatteryOptimizeUtils batteryOptimizeUtils = - mBatteryOptimizeUtils != null - ? mBatteryOptimizeUtils /*testing only*/ - : new BatteryOptimizeUtils(mContext, uid, packageName); batteryOptimizeUtils.setAppUsageState( mode, BatteryOptimizeHistoricalLogEntry.Action.RESTORE); Log.d(TAG, String.format("restore:%s mode=%d", packageName, mode)); @@ -294,8 +313,4 @@ public final class BatteryBackupHelper implements BackupHelper { Log.e(TAG, "writeBackupData() is failed for " + dataKey, e); } } - - private static boolean isOwner() { - return UserHandle.myUserId() == UserHandle.USER_SYSTEM; - } } diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java index b9ac64dd781..00611de7817 100644 --- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java @@ -31,11 +31,14 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settings.R; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.List; /** A utility class for application usage operation. */ public class BatteryOptimizeUtils { @@ -214,6 +217,11 @@ public class BatteryOptimizeUtils { || powerAllowlistBackend.isDefaultActiveApp(packageName, uid); } + static List getAllowList(Context context) { + return Arrays.asList(context.getResources().getStringArray( + R.array.config_disable_optimization_mode_apps)); + } + private static void setAppUsageStateInternal( Context context, @OptimizationMode int mode, int uid, String packageName, BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend, diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java index c54e6d8c503..4b9e6efaff0 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java @@ -23,16 +23,27 @@ import android.content.Intent; import android.provider.Settings; import android.util.Log; +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController; import com.android.settingslib.fuelgauge.BatterySaverUtils; +import java.util.List; + /** Execute battery settings migration tasks in the device booting stage. */ public final class BatterySettingsMigrateChecker extends BroadcastReceiver { private static final String TAG = "BatterySettingsMigrateChecker"; + @VisibleForTesting + static BatteryOptimizeUtils sBatteryOptimizeUtils = null; + @Override public void onReceive(Context context, Intent intent) { - if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { + if (intent != null + && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()) + && BatteryBackupHelper.isOwner()) { verifyConfiguration(context); } } @@ -40,9 +51,35 @@ public final class BatterySettingsMigrateChecker extends BroadcastReceiver { static void verifyConfiguration(Context context) { context = context.getApplicationContext(); verifySaverConfiguration(context); + verifyOptimizationModes(context); } - private static void verifySaverConfiguration(Context context) { + /** Avoid users set important apps into the unexpected battery optimize modes */ + static void verifyOptimizationModes(Context context) { + Log.d(TAG, "invoke verifyOptimizationModes()"); + verifyOptimizationModes(context, BatteryOptimizeUtils.getAllowList(context)); + } + + @VisibleForTesting + static void verifyOptimizationModes(Context context, List allowList) { + allowList.forEach(packageName -> { + final BatteryOptimizeUtils batteryOptimizeUtils = + BatteryBackupHelper.newBatteryOptimizeUtils(context, packageName, + /* testOptimizeUtils */ sBatteryOptimizeUtils); + if (batteryOptimizeUtils == null) { + return; + } + if (batteryOptimizeUtils.getAppOptimizationMode() != + BatteryOptimizeUtils.MODE_OPTIMIZED) { + Log.w(TAG, "Reset optimization mode for: " + packageName); + batteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, + BatteryOptimizeHistoricalLogEntry.Action.FORCE_RESET); + } + }); + } + + static void verifySaverConfiguration(Context context) { + Log.d(TAG, "invoke verifySaverConfiguration()"); final ContentResolver resolver = context.getContentResolver(); final int threshold = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java index dfee3e797b9..c34dcecce73 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsMigrateCheckerTest.java @@ -18,35 +18,76 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.os.UserManager; import com.android.settings.TestUtils; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.util.ArrayList; +import java.util.Arrays; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {BatterySettingsMigrateCheckerTest.ShadowUserHandle.class}) public final class BatterySettingsMigrateCheckerTest { private static final Intent BOOT_COMPLETED_INTENT = new Intent(Intent.ACTION_BOOT_COMPLETED); + private static final int UID = 2003; + private static final String PACKAGE_NAME = "com.android.test.app"; private Context mContext; private BatterySettingsMigrateChecker mBatterySettingsMigrateChecker; + @Mock + private PackageManager mPackageManager; + @Mock + private BatteryOptimizeUtils mBatteryOptimizeUtils; + @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); + doReturn(mContext).when(mContext).getApplicationContext(); + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(UID).when(mPackageManager) + .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); + BatterySettingsMigrateChecker.sBatteryOptimizeUtils = mBatteryOptimizeUtils; mBatterySettingsMigrateChecker = new BatterySettingsMigrateChecker(); } + @After + public void resetShadows() { + ShadowUserHandle.reset(); + } + @Test public void onReceive_invalidScheduledLevel_resetScheduledValue() { final int invalidScheduledLevel = 5; @@ -98,6 +139,54 @@ public final class BatterySettingsMigrateCheckerTest { assertThat(getScheduledLevel()).isEqualTo(invalidScheduledLevel); } + @Test + public void onReceive_nonOwner_noAction() { + ShadowUserHandle.setUid(1); + final int invalidScheduledLevel = 5; + setScheduledLevel(invalidScheduledLevel); + + mBatterySettingsMigrateChecker.onReceive(mContext, BOOT_COMPLETED_INTENT); + + assertThat(getScheduledLevel()).isEqualTo(invalidScheduledLevel); + } + + @Test + public void verifyOptimizationModes_inAllowList_resetOptimizationMode() throws Exception { + doReturn(BatteryOptimizeUtils.MODE_RESTRICTED).when(mBatteryOptimizeUtils) + .getAppOptimizationMode(); + + mBatterySettingsMigrateChecker.verifyOptimizationModes( + mContext, Arrays.asList(PACKAGE_NAME)); + + final InOrder inOrder = inOrder(mBatteryOptimizeUtils); + inOrder.verify(mBatteryOptimizeUtils).getAppOptimizationMode(); + inOrder.verify(mBatteryOptimizeUtils).setAppUsageState( + BatteryOptimizeUtils.MODE_OPTIMIZED, + BatteryOptimizeHistoricalLogEntry.Action.FORCE_RESET); + } + + @Test + public void verifyOptimizationModes_optimizedMode_noAction() throws Exception { + doReturn(BatteryOptimizeUtils.MODE_OPTIMIZED).when(mBatteryOptimizeUtils) + .getAppOptimizationMode(); + + mBatterySettingsMigrateChecker.verifyOptimizationModes( + mContext, Arrays.asList(PACKAGE_NAME)); + + verify(mBatteryOptimizeUtils, never()).setAppUsageState(anyInt(), any()); + } + + @Test + public void verifyOptimizationModes_notInAllowList_noAction() throws Exception { + doReturn(BatteryOptimizeUtils.MODE_RESTRICTED).when(mBatteryOptimizeUtils) + .getAppOptimizationMode(); + + mBatterySettingsMigrateChecker.verifyOptimizationModes( + mContext, new ArrayList()); + + verifyNoInteractions(mBatteryOptimizeUtils); + } + private void setScheduledLevel(int scheduledLevel) { TestUtils.setScheduledLevel(mContext, scheduledLevel); } @@ -105,4 +194,24 @@ public final class BatterySettingsMigrateCheckerTest { private int getScheduledLevel() { return TestUtils.getScheduledLevel(mContext); } + + @Implements(UserHandle.class) + public static class ShadowUserHandle { + // Sets the default as thte OWNER role. + private static int sUid = 0; + + public static void setUid(int uid) { + sUid = uid; + } + + @Implementation + public static int myUserId() { + return sUid; + } + + @Resetter + public static void reset() { + sUid = 0; + } + } } From 0d1638c3d32439740b90900f5bbc31821357a72a Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Mon, 15 May 2023 07:23:32 +0000 Subject: [PATCH 06/18] [Use] Replace settings LocalePreference with androidx LocalePreference Bug: 263861083 Test: atest and manual test Change-Id: I7af48074278df0b12c83500f0d2b61eb279e70be --- .../FirstDayOfWeekController.java | 2 + .../LocalePreferences.java | 605 ------------------ .../RegionalPreferencesDataUtils.java | 2 + .../TemperatureUnitController.java | 2 + .../RegionalPreferencesDataUtilsTest.java | 1 + 5 files changed, 7 insertions(+), 605 deletions(-) delete mode 100644 src/com/android/settings/regionalpreferences/LocalePreferences.java diff --git a/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java b/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java index 1ebd1665aea..574e62f9aa9 100644 --- a/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java +++ b/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java @@ -19,6 +19,8 @@ package com.android.settings.regionalpreferences; import android.content.Context; import android.provider.Settings; +import androidx.core.text.util.LocalePreferences; + import com.android.settings.R; import com.android.settings.core.BasePreferenceController; diff --git a/src/com/android/settings/regionalpreferences/LocalePreferences.java b/src/com/android/settings/regionalpreferences/LocalePreferences.java deleted file mode 100644 index 20062218334..00000000000 --- a/src/com/android/settings/regionalpreferences/LocalePreferences.java +++ /dev/null @@ -1,605 +0,0 @@ -/* - * 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.regionalpreferences; -import android.icu.number.LocalizedNumberFormatter; -import android.icu.number.NumberFormatter; -import android.icu.text.DateFormat; -import android.icu.text.DateTimePatternGenerator; -import android.icu.util.MeasureUnit; -import android.os.Build.VERSION; - -import androidx.annotation.DoNotInline; -import androidx.annotation.NonNull; -import androidx.annotation.OptIn; -import androidx.annotation.RestrictTo; -import androidx.annotation.StringDef; -import androidx.core.os.BuildCompat; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Locale; -import java.util.Locale.Category; - -/** - * TODO(b/263861083) This is a temp file and will replace it to Androidx version. - * Provides friendly APIs to get the user's locale preferences. The data can refer to - * external/cldr/common/main/en.xml. - */ -public final class LocalePreferences { - private static final String TAG = LocalePreferences.class.getSimpleName(); - - /** APIs to get the user's preference of the hour cycle. */ - public static class HourCycle { - private static final String U_EXTENSION_OF_HOUR_CYCLE = "hc"; - - /** 12 Hour System (0-11) */ - public static final String H11 = "h11"; - /** 12 Hour System (1-12) */ - public static final String H12 = "h12"; - /** 24 Hour System (0-23) */ - public static final String H23 = "h23"; - /** 24 Hour System (1-24) */ - public static final String H24 = "h24"; - /** Default hour cycle for the locale */ - public static final String DEFAULT = ""; - - /** @hide */ - @RestrictTo(RestrictTo.Scope.LIBRARY) - @StringDef({ - H11, - H12, - H23, - H24, - DEFAULT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface HourCycleTypes { - } - - private HourCycle() { - } - } - - /** - * Return the user's preference of the hour cycle which is from - * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and - * bases on the {@code Locale#getDefault(Locale.Category)}. E.g. "h23" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @HourCycle.HourCycleTypes - public static String getHourCycle() { - return getHourCycle(true); - } - - /** - * Return the hour cycle setting of the inputted {@link Locale}. The returned result is resolved - * and bases on the inputted {@code Locale}. - * E.g. "h23" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @HourCycle.HourCycleTypes - public static String getHourCycle(@NonNull Locale locale) { - return getHourCycle(locale, true); - } - - /** - * Return the user's preference of the hour cycle which is from - * {@link Locale#getDefault(Locale.Category)}. E.g. "h23" - * - * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains hour cycle subtag, - * this argument is ignored. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag - * and the resolved argument is true, this function tries to find the default - * hour cycle for the {@code Locale#getDefault(Locale.Category)}. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag - * and the resolved argument is false, this function returns empty string - * i.e. HourCycle.Default. - * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified - * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string - * i.e. HourCycle.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @HourCycle.HourCycleTypes - public static String getHourCycle( - boolean resolved) { - return getHourCycle(Api33Impl.getDefaultLocale(), resolved); - } - - /** - * Return the hour cycle setting of the inputted {@link Locale}. E.g. "en-US-u-hc-h23". - * - * @param locale The {@code Locale} to get the hour cycle. - * @param resolved If the given {@code Locale} contains hour cycle subtag, this argument is - * ignored. If the given {@code Locale} doesn't contain hour cycle subtag and - * the resolved argument is true, this function tries to find the default - * hour cycle for the given {@code Locale}. If the given {@code Locale} doesn't - * contain hour cycle subtag and the resolved argument is false, this function - * return empty string i.e. HourCycle.Default. - * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified - * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string - * i.e. HourCycle.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @HourCycle.HourCycleTypes - public static String getHourCycle(@NonNull Locale locale, boolean resolved) { - if (!BuildCompat.isAtLeastT()) { - throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT); - } - return Api33Impl.getHourCycle(locale, resolved); - } - - /** APIs to get the user's preference of Calendar. */ - public static class CalendarType { - private static final String U_EXTENSION_OF_CALENDAR = "ca"; - /** Chinese Calendar */ - public static final String CHINESE = "chinese"; - /** Dangi Calendar (Korea Calendar) */ - public static final String DANGI = "dangi"; - /** Gregorian Calendar */ - public static final String GREGORIAN = "gregorian"; - /** Hebrew Calendar */ - public static final String HEBREW = "hebrew"; - /** Indian National Calendar */ - public static final String INDIAN = "indian"; - /** Islamic Calendar */ - public static final String ISLAMIC = "islamic"; - /** Islamic Calendar (tabular, civil epoch) */ - public static final String ISLAMIC_CIVIL = "islamic-civil"; - /** Islamic Calendar (Saudi Arabia, sighting) */ - public static final String ISLAMIC_RGSA = "islamic-rgsa"; - /** Islamic Calendar (tabular, astronomical epoch) */ - public static final String ISLAMIC_TBLA = "islamic-tbla"; - /** Islamic Calendar (Umm al-Qura) */ - public static final String ISLAMIC_UMALQURA = "islamic-umalqura"; - /** Persian Calendar */ - public static final String PERSIAN = "persian"; - /** Default calendar for the locale */ - public static final String DEFAULT = ""; - - /** @hide */ - @RestrictTo(RestrictTo.Scope.LIBRARY) - @StringDef({ - CHINESE, - DANGI, - GREGORIAN, - HEBREW, - INDIAN, - ISLAMIC, - ISLAMIC_CIVIL, - ISLAMIC_RGSA, - ISLAMIC_TBLA, - ISLAMIC_UMALQURA, - PERSIAN, - DEFAULT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CalendarTypes { - } - - private CalendarType() { - } - } - - /** - * Return the user's preference of the calendar type which is from {@link - * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on - * the {@code Locale#getDefault(Locale.Category)} settings. E.g. "chinese" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @CalendarType.CalendarTypes - public static String getCalendarType() { - return getCalendarType(true); - } - - /** - * Return the calendar type of the inputted {@link Locale}. The returned result is resolved and - * bases on the inputted {@link Locale} settings. - * E.g. "chinese" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @CalendarType.CalendarTypes - public static String getCalendarType(@NonNull Locale locale) { - return getCalendarType(locale, true); - } - - /** - * Return the user's preference of the calendar type which is from {@link - * Locale#getDefault(Locale.Category)}. E.g. "chinese" - * - * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains calendar type - * subtag, this argument is ignored. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type - * subtag and the resolved argument is true, this function tries to find - * the default calendar type for the - * {@code Locale#getDefault(Locale.Category)}. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type - * subtag and the resolved argument is false, this function returns empty string - * i.e. CalendarTypes.Default. - * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was - * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns - * empty string i.e. CalendarTypes.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @CalendarType.CalendarTypes - public static String getCalendarType(boolean resolved) { - return getCalendarType(Api33Impl.getDefaultLocale(), resolved); - } - - /** - * Return the calendar type of the inputted {@link Locale}. E.g. "chinese" - * - * @param locale The {@link Locale} to get the calendar type. - * @param resolved If the given {@code Locale} contains calendar type subtag, this argument is - * ignored. If the given {@code Locale} doesn't contain calendar type subtag and - * the resolved argument is true, this function tries to find the default - * calendar type for the given {@code Locale}. If the given {@code Locale} - * doesn't contain calendar type subtag and the resolved argument is false, this - * function return empty string i.e. CalendarTypes.Default. - * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was - * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns - * empty string i.e. CalendarTypes.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @CalendarType.CalendarTypes - public static String getCalendarType(@NonNull Locale locale, boolean resolved) { - if (!BuildCompat.isAtLeastT()) { - throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT); - } - return Api33Impl.getCalendarType(locale, resolved); - } - - /** APIs to get the user's preference of temperature unit. */ - public static class TemperatureUnit { - private static final String U_EXTENSION_OF_TEMPERATURE_UNIT = "mu"; - /** Celsius */ - public static final String CELSIUS = "celsius"; - /** Fahrenheit */ - public static final String FAHRENHEIT = "fahrenhe"; - /** Kelvin */ - public static final String KELVIN = "kelvin"; - /** Default Temperature for the locale */ - public static final String DEFAULT = ""; - - /** @hide */ - @RestrictTo(RestrictTo.Scope.LIBRARY) - @StringDef({ - CELSIUS, - FAHRENHEIT, - KELVIN, - DEFAULT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TemperatureUnits { - } - - private TemperatureUnit() { - } - } - - /** - * Return the user's preference of the temperature unit which is from {@link - * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the - * {@code Locale#getDefault(Locale.Category)} settings. E.g. "fahrenhe" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @TemperatureUnit.TemperatureUnits - public static String getTemperatureUnit() { - return getTemperatureUnit(true); - } - - /** - * Return the temperature unit of the inputted {@link Locale}. The returned result is resolved - * and bases on the inputted {@code Locale} settings. E.g. "fahrenhe" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @TemperatureUnit.TemperatureUnits - public static String getTemperatureUnit( - @NonNull Locale locale) { - return getTemperatureUnit(locale, true); - } - - /** - * Return the user's preference of the temperature unit which is from {@link - * Locale#getDefault(Locale.Category)}. E.g. "fahrenhe" - * - * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains temperature unit - * subtag, this argument is ignored. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit - * subtag and the resolved argument is true, this function tries to find - * the default temperature unit for the - * {@code Locale#getDefault(Locale.Category)}. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit - * subtag and the resolved argument is false, this function returns empty string - * i.e. TemperatureUnits.Default. - * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was - * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns - * empty string i.e. TemperatureUnits.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @TemperatureUnit.TemperatureUnits - public static String getTemperatureUnit(boolean resolved) { - return getTemperatureUnit(Api33Impl.getDefaultLocale(), resolved); - } - - /** - * Return the temperature unit of the inputted {@link Locale}. E.g. "fahrenheit" - * - * @param locale The {@link Locale} to get the temperature unit. - * @param resolved If the given {@code Locale} contains temperature unit subtag, this argument - * is ignored. If the given {@code Locale} doesn't contain temperature unit - * subtag and the resolved argument is true, this function tries to find - * the default temperature unit for the given {@code Locale}. If the given - * {@code Locale} doesn't contain temperature unit subtag and the resolved - * argument is false, this function return empty string - * i.e. TemperatureUnits.Default. - * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was - * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns - * empty string i.e. TemperatureUnits.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @TemperatureUnit.TemperatureUnits - public static String getTemperatureUnit(@NonNull Locale locale, boolean resolved) { - if (!BuildCompat.isAtLeastT()) { - throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT); - } - return Api33Impl.getTemperatureUnit(locale, resolved); - } - - /** APIs to get the user's preference of the first day of week. */ - public static class FirstDayOfWeek { - private static final String U_EXTENSION_OF_FIRST_DAY_OF_WEEK = "fw"; - /** Sunday */ - public static final String SUNDAY = "sun"; - /** Monday */ - public static final String MONDAY = "mon"; - /** Tuesday */ - public static final String TUESDAY = "tue"; - /** Wednesday */ - public static final String WEDNESDAY = "wed"; - /** Thursday */ - public static final String THURSDAY = "thu"; - /** Friday */ - public static final String FRIDAY = "fri"; - /** Saturday */ - public static final String SATURDAY = "sat"; - /** Default first day of week for the locale */ - public static final String DEFAULT = ""; - - /** @hide */ - @RestrictTo(RestrictTo.Scope.LIBRARY) - @StringDef({ - SUNDAY, - MONDAY, - TUESDAY, - WEDNESDAY, - THURSDAY, - FRIDAY, - SATURDAY, - DEFAULT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Days { - } - - private FirstDayOfWeek() { - } - } - - /** - * Return the user's preference of the first day of week which is from - * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the - * {@code Locale#getDefault(Locale.Category)} settings. E.g. "sun" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @FirstDayOfWeek.Days - public static String getFirstDayOfWeek() { - return getFirstDayOfWeek(true); - } - - /** - * Return the first day of week of the inputted {@link Locale}. The returned result is resolved - * and bases on the inputted {@code Locale} settings. - * E.g. "sun" - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - public static @FirstDayOfWeek.Days String getFirstDayOfWeek(@NonNull Locale locale) { - return getFirstDayOfWeek(locale, true); - } - - /** - * Return the user's preference of the first day of week which is from {@link - * Locale#getDefault(Locale.Category)}. E.g. "sun" - * - * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains first day of week - * subtag, this argument is ignored. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week - * subtag and the resolved argument is true, this function tries to find - * the default first day of week for the - * {@code Locale#getDefault(Locale.Category)}. If the - * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week - * subtag and the resolved argument is false, this function returns empty string - * i.e. Days.Default. - * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was specified - * in the first day of week subtag, e.g. en-US-u-fw-days, this function returns empty string - * i.e. Days.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @FirstDayOfWeek.Days - public static String getFirstDayOfWeek(boolean resolved) { - return getFirstDayOfWeek(Api33Impl.getDefaultLocale(), resolved); - } - - /** - * Return the first day of week of the inputted {@link Locale}. E.g. "sun" - * - * @param locale The {@link Locale} to get the first day of week. - * @param resolved If the given {@code Locale} contains first day of week subtag, this argument - * is ignored. If the given {@code Locale} doesn't contain first day of week - * subtag and the resolved argument is true, this function tries to find - * the default first day of week for the given {@code Locale}. If the given - * {@code Locale} doesn't contain first day of week subtag and the resolved - * argument is false, this function return empty string i.e. Days.Default. - * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was - * specified in the first day of week subtag, e.g. en-US-u-fw-days, this function returns - * empty string i.e. Days.Default. - */ - @NonNull - @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class) - @FirstDayOfWeek.Days - public static String getFirstDayOfWeek( - @NonNull Locale locale, boolean resolved) { - if (!BuildCompat.isAtLeastT()) { - throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT); - } - - return Api33Impl.getFirstDayOfWeek(locale, resolved); - } - - private static class Api33Impl { - @DoNotInline - @HourCycle.HourCycleTypes - static String getHourCycle(@NonNull Locale locale, - boolean resolved) { - String hc = locale.getUnicodeLocaleType(HourCycle.U_EXTENSION_OF_HOUR_CYCLE); - if (hc != null) { - return hc; - } - if (!resolved) { - return HourCycle.DEFAULT; - } - - return getHourCycleType( - DateTimePatternGenerator.getInstance(locale).getDefaultHourCycle()); - - } - - @DoNotInline - @CalendarType.CalendarTypes - static String getCalendarType(@NonNull Locale locale, boolean resolved) { - String ca = locale.getUnicodeLocaleType(CalendarType.U_EXTENSION_OF_CALENDAR); - if (ca != null) { - return ca; - } - if (!resolved) { - return CalendarType.DEFAULT; - } - - return android.icu.util.Calendar.getInstance(locale).getType(); - } - - @DoNotInline - @TemperatureUnit.TemperatureUnits - static String getTemperatureUnit(@NonNull Locale locale, boolean resolved) { - String mu = - locale.getUnicodeLocaleType(TemperatureUnit.U_EXTENSION_OF_TEMPERATURE_UNIT); - if (mu != null) { - return mu; - } - if (!resolved) { - return TemperatureUnit.DEFAULT; - } - - return getResolvedTemperatureUnit(locale); - } - - @DoNotInline - @FirstDayOfWeek.Days - static String getFirstDayOfWeek(@NonNull Locale locale, boolean resolved) { - String mu = - locale.getUnicodeLocaleType(FirstDayOfWeek.U_EXTENSION_OF_FIRST_DAY_OF_WEEK); - if (mu != null) { - return mu; - } - if (!resolved) { - return FirstDayOfWeek.DEFAULT; - } - // TODO(b/262294472) Use {@code android.icu.util.Calendar} instead of - // {@code java.util.Calendar}. - return getStringOfFirstDayOfWeek( - java.util.Calendar.getInstance(locale).getFirstDayOfWeek()); - } - - @DoNotInline - static Locale getDefaultLocale() { - return Locale.getDefault(Category.FORMAT); - } - - private static String getStringOfFirstDayOfWeek(int fw) { - String[] arrDays = { - FirstDayOfWeek.SUNDAY, - FirstDayOfWeek.MONDAY, - FirstDayOfWeek.TUESDAY, - FirstDayOfWeek.WEDNESDAY, - FirstDayOfWeek.THURSDAY, - FirstDayOfWeek.FRIDAY, - FirstDayOfWeek.SATURDAY}; - - return fw >= 1 && fw <= 7 ? arrDays[fw - 1] : FirstDayOfWeek.DEFAULT; - } - - @HourCycle.HourCycleTypes - private static String getHourCycleType( - DateFormat.HourCycle hourCycle) { - switch (hourCycle) { - case HOUR_CYCLE_11: - return HourCycle.H11; - case HOUR_CYCLE_12: - return HourCycle.H12; - case HOUR_CYCLE_23: - return HourCycle.H23; - case HOUR_CYCLE_24: - return HourCycle.H24; - default: - return HourCycle.DEFAULT; - } - } - - @TemperatureUnit.TemperatureUnits - private static String getResolvedTemperatureUnit(@NonNull Locale locale) { - LocalizedNumberFormatter nf = NumberFormatter.with() - .usage("temperature") - .unit(MeasureUnit.CELSIUS) - .locale(locale); - String unit = nf.format(1).getOutputUnit().getIdentifier(); - if (unit.contains(TemperatureUnit.FAHRENHEIT)) { - return TemperatureUnit.FAHRENHEIT; - } - return unit; - } - - private Api33Impl() { - } - } - - private LocalePreferences() { - } -} diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java b/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java index 32e00b4a29f..d1ae40b2761 100644 --- a/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java +++ b/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java @@ -22,6 +22,8 @@ import android.os.LocaleList; import android.provider.Settings; import android.text.TextUtils; +import androidx.core.text.util.LocalePreferences; + import com.android.internal.app.LocalePicker; import com.android.settings.R; diff --git a/src/com/android/settings/regionalpreferences/TemperatureUnitController.java b/src/com/android/settings/regionalpreferences/TemperatureUnitController.java index 9fbf69b3c44..4b0f22c8a65 100644 --- a/src/com/android/settings/regionalpreferences/TemperatureUnitController.java +++ b/src/com/android/settings/regionalpreferences/TemperatureUnitController.java @@ -19,6 +19,8 @@ package com.android.settings.regionalpreferences; import android.content.Context; import android.provider.Settings; +import androidx.core.text.util.LocalePreferences; + import com.android.settings.R; import com.android.settings.core.BasePreferenceController; diff --git a/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtilsTest.java b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtilsTest.java index 85272e426c0..c9571be099b 100644 --- a/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtilsTest.java +++ b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtilsTest.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.LocaleList; import android.provider.Settings; +import androidx.core.text.util.LocalePreferences; import androidx.test.core.app.ApplicationProvider; import com.android.internal.app.LocalePicker; From 5cd3a7298e250ae8ddf4d6118ff726670052ce84 Mon Sep 17 00:00:00 2001 From: ykhung Date: Mon, 15 May 2023 15:02:52 +0800 Subject: [PATCH 07/18] Disable the optimization mode preference if the app is not settable https://screenshot.googleplex.com/BbQyEv6YCjPVwUF (allowlist) https://screenshot.googleplex.com/3hTb8TQwGMQqcwS https://screenshot.googleplex.com/6hdTAT2yNFucD9N (system app) https://screenshot.googleplex.com/77T5JyAD5nZJpDT (normal app) Fix: 281566984 Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge.* Change-Id: If7aa4b61bc806daa570187da24b7f454cd069dec --- .../fuelgauge/AdvancedPowerUsageDetail.java | 2 +- .../settings/fuelgauge/BatteryOptimizeUtils.java | 9 ++++----- .../fuelgauge/OptimizedPreferenceController.java | 4 ++-- .../fuelgauge/RestrictedPreferenceController.java | 5 ++--- .../fuelgauge/UnrestrictedPreferenceController.java | 4 ++-- .../fuelgauge/AdvancedPowerUsageDetailTest.java | 6 +++--- .../fuelgauge/BatteryOptimizeUtilsTest.java | 8 ++++---- .../OptimizedPreferenceControllerTest.java | 10 +++++----- .../RestrictedPreferenceControllerTest.java | 12 ++++++------ .../UnrestrictedPreferenceControllerTest.java | 13 ++++++------- 10 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 777e4281764..79e01940ecd 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -343,7 +343,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements final String stateString; final String footerString; - if (!mBatteryOptimizeUtils.isValidPackageName()) { + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { // Present optimized only string when the package name is invalid. stateString = context.getString(R.string.manager_battery_usage_optimized_only); footerString = context.getString( diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java index 00611de7817..589e1fd4055 100644 --- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java @@ -128,11 +128,10 @@ public class BatteryOptimizeUtils { mContext, mode, mUid, mPackageName, mBatteryUtils, mPowerAllowListBackend, action); } - /** - * Return {@code true} if package name is valid (can get an uid). - */ - public boolean isValidPackageName() { - return mBatteryUtils.getPackageUid(mPackageName) != BatteryUtils.UID_NULL; + /** Return {@code true} if it is disabled for default optimized mode only. */ + public boolean isDisabledForOptimizeModeOnly() { + return getAllowList(mContext).contains(mPackageName) + || mBatteryUtils.getPackageUid(mPackageName) == BatteryUtils.UID_NULL; } /** diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java index 88241b6ff39..ca75b0e8d22 100644 --- a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java @@ -46,8 +46,8 @@ public class OptimizedPreferenceController extends AbstractPreferenceController @Override public void updateState(Preference preference) { - if (!mBatteryOptimizeUtils.isValidPackageName()) { - Log.d(TAG, "invalid package name, optimized states only"); + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); preference.setEnabled(true); ((SelectorWithWidgetPreference) preference).setChecked(true); return; diff --git a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java index fe896a6aed0..7db77f15e40 100644 --- a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package com.android.settings.fuelgauge; import android.content.Context; @@ -43,8 +42,8 @@ public class RestrictedPreferenceController extends AbstractPreferenceController @Override public void updateState(Preference preference) { - if (!mBatteryOptimizeUtils.isValidPackageName()) { - Log.d(TAG, "invalid package name, disable pref"); + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); preference.setEnabled(false); return; } else { diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java index be4091c782f..4578723ce5e 100644 --- a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java +++ b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java @@ -42,8 +42,8 @@ public class UnrestrictedPreferenceController extends AbstractPreferenceControll @Override public void updateState(Preference preference) { - if (!mBatteryOptimizeUtils.isValidPackageName()) { - Log.d(TAG, "invalid package name, disable pref"); + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + Log.d(TAG, "disable preference for " + mBatteryOptimizeUtils.getPackageName()); preference.setEnabled(false); return; } else { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 0278553b2a5..5e9fb738672 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -710,7 +710,7 @@ public class AdvancedPowerUsageDetailTest { @Test public void initPreferenceForTriState_isValidPackageName_hasCorrectString() { - when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(false); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); mFragment.initPreferenceForTriState(mContext); @@ -720,7 +720,7 @@ public class AdvancedPowerUsageDetailTest { @Test public void initPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() { - when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); mFragment.initPreferenceForTriState(mContext); @@ -731,7 +731,7 @@ public class AdvancedPowerUsageDetailTest { @Test public void initPreferenceForTriState_hasCorrectString() { - when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); mFragment.initPreferenceForTriState(mContext); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java index 83a75f61e96..f9d3108f904 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java @@ -136,16 +136,16 @@ public class BatteryOptimizeUtilsTest { } @Test - public void testIsValidPackageName_InvalidPackageName_returnFalse() { + public void isDisabledForOptimizeModeOnly_invalidPackageName_returnTrue() { final BatteryOptimizeUtils testBatteryOptimizeUtils = new BatteryOptimizeUtils(mContext, UID, null); - assertThat(testBatteryOptimizeUtils.isValidPackageName()).isFalse(); + assertThat(testBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).isTrue(); } @Test - public void testIsValidPackageName_validPackageName_returnTrue() { - assertThat(mBatteryOptimizeUtils.isValidPackageName()).isTrue(); + public void isDisabledForOptimizeModeOnly_validPackageName_returnFalse() { + assertThat(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).isFalse(); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java index 1fec92a8134..71bb998e4b8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java @@ -52,7 +52,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_invalidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); mController.updateState(mPreference); @@ -62,7 +62,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultAppAndOptimizeStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_OPTIMIZED); @@ -74,7 +74,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); mController.updateState(mPreference); @@ -85,7 +85,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_isOptimizedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_OPTIMIZED); @@ -96,7 +96,7 @@ public class OptimizedPreferenceControllerTest { @Test public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java index 944376c77dd..bcddbc2500b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java @@ -52,7 +52,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_isValidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); @@ -61,7 +61,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_invalidPackage_prefDisabled() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); mController.updateState(mPreference); @@ -70,7 +70,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_RESTRICTED); @@ -82,7 +82,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); mController.updateState(mPreference); @@ -93,7 +93,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_isRestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_RESTRICTED); @@ -104,7 +104,7 @@ public class RestrictedPreferenceControllerTest { @Test public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java index c5642df576c..9bed9bad152 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; @@ -53,7 +52,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_isValidPackage_prefEnabled() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); @@ -62,7 +61,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_invalidPackage_prefDisabled() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); mController.updateState(mPreference); @@ -71,7 +70,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultAppAndUnrestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_UNRESTRICTED); @@ -83,7 +82,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); mController.updateState(mPreference); @@ -94,7 +93,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_isUnrestrictedStates_prefChecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn( BatteryOptimizeUtils.MODE_UNRESTRICTED); @@ -105,7 +104,7 @@ public class UnrestrictedPreferenceControllerTest { @Test public void testUpdateState_prefUnchecked() { - when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); + when(mockBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); mController.updateState(mPreference); From 98e4d7bf1672e456a4736f0514dcfd341df1fa88 Mon Sep 17 00:00:00 2001 From: Ankita Vyas Date: Mon, 15 May 2023 13:11:14 +0000 Subject: [PATCH 08/18] Fetch summary only if feature is available onResume(loads summary) of controller seems to be invoked even when availabilty status is unspported. Bug: 278294334 Test: manual Change-Id: I2486a7d61bea1b981ae56f0ff32b9d411be2ad14 --- .../settings/applications/ClonedAppsPreferenceController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/settings/applications/ClonedAppsPreferenceController.java b/src/com/android/settings/applications/ClonedAppsPreferenceController.java index a8a79f487e7..332df1837b5 100644 --- a/src/com/android/settings/applications/ClonedAppsPreferenceController.java +++ b/src/com/android/settings/applications/ClonedAppsPreferenceController.java @@ -75,6 +75,9 @@ public class ClonedAppsPreferenceController extends BasePreferenceController } private void updatePreferenceSummary() { + if (!isAvailable()) { + return; + } new AsyncTask() { @Override From 2c125d29a3c449567c492a5b6690ac0ea6258e56 Mon Sep 17 00:00:00 2001 From: ykhung Date: Mon, 15 May 2023 23:33:30 +0800 Subject: [PATCH 09/18] Remove useless optimization mode backup for FULL_POWER_LIST Remove the useless backup for the FULL_POWER_LIST, since we will not use it to restore data, only backup the optimization_mode_list is enough Test: presubmit Bug: 192523697 Change-Id: I95f1f41732b8aff090e8b722896d153f6181a6ac --- .../fuelgauge/BatteryBackupHelper.java | 10 ++----- .../fuelgauge/BatteryBackupHelperTest.java | 30 +------------------ 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java index 1bb3b4de001..eb4bbf687d7 100644 --- a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java +++ b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java @@ -55,7 +55,6 @@ public final class BatteryBackupHelper implements BackupHelper { static final String DELIMITER = ","; static final String DELIMITER_MODE = ":"; - static final String KEY_FULL_POWER_LIST = "full_power_list"; static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list"; @VisibleForTesting @@ -83,7 +82,7 @@ public final class BatteryBackupHelper implements BackupHelper { Log.w(TAG, "ignore performBackup() for non-owner or empty data"); return; } - final List allowlistedApps = backupFullPowerList(data); + final List allowlistedApps = getFullPowerList(); if (allowlistedApps != null) { backupOptimizationMode(data, allowlistedApps); } @@ -117,7 +116,7 @@ public final class BatteryBackupHelper implements BackupHelper { public void writeNewStateDescription(ParcelFileDescriptor newState) { } - private List backupFullPowerList(BackupDataOutput data) { + private List getFullPowerList() { final long timestamp = System.currentTimeMillis(); String[] allowlistedApps; try { @@ -131,10 +130,7 @@ public final class BatteryBackupHelper implements BackupHelper { Log.w(TAG, "no data found in the getFullPowerList()"); return new ArrayList<>(); } - - final String allowedApps = String.join(DELIMITER, allowlistedApps); - writeBackupData(data, KEY_FULL_POWER_LIST, allowedApps); - Log.d(TAG, String.format("backup getFullPowerList() size=%d in %d/ms", + Log.d(TAG, String.format("getFullPowerList() size=%d in %d/ms", allowlistedApps.length, (System.currentTimeMillis() - timestamp))); return Arrays.asList(allowlistedApps); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java index 89707305add..a800fdbd2cc 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java @@ -159,34 +159,6 @@ public final class BatteryBackupHelperTest { verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); } - @Test - public void performBackup_oneFullPowerListElement_backupFullPowerListData() - throws Exception { - final String[] fullPowerList = {"com.android.package"}; - doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist(); - - mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null); - - final byte[] expectedBytes = fullPowerList[0].getBytes(); - verify(mBackupDataOutput).writeEntityHeader( - BatteryBackupHelper.KEY_FULL_POWER_LIST, expectedBytes.length); - verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); - } - - @Test - public void performBackup_backupFullPowerListData() throws Exception { - final String[] fullPowerList = {"com.android.package1", "com.android.package2"}; - doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist(); - - mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null); - - final String expectedResult = fullPowerList[0] + DELIMITER + fullPowerList[1]; - final byte[] expectedBytes = expectedResult.getBytes(); - verify(mBackupDataOutput).writeEntityHeader( - BatteryBackupHelper.KEY_FULL_POWER_LIST, expectedBytes.length); - verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); - } - @Test public void performBackup_nonOwner_ignoreAllBackupAction() throws Exception { ShadowUserHandle.setUid(1); @@ -283,7 +255,7 @@ public final class BatteryBackupHelperTest { @Test public void restoreEntity_incorrectDataKey_notReadBackupData() throws Exception { - final String incorrectDataKey = BatteryBackupHelper.KEY_FULL_POWER_LIST; + final String incorrectDataKey = "incorrect_data_key"; mockBackupData(30 /*dataSize*/, incorrectDataKey); mBatteryBackupHelper.restoreEntity(mBackupDataInputStream); From cad41681d60fe239abb6de33e4037d2afb6933f4 Mon Sep 17 00:00:00 2001 From: ykhung Date: Tue, 16 May 2023 08:36:00 +0800 Subject: [PATCH 10/18] Add device build information in the backup stage Insert the device build information in the battery optimization mode backup stage, such that we can use it to decide whether we should restore the data in the targeted device or not Bug: 192523697 Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge.* Change-Id: I3ab76e013ea9aca4d336a62e0c7cb6882c5b5085 --- .../fuelgauge/BatteryBackupHelper.java | 29 +++++++++++- .../fuelgauge/PowerUsageFeatureProvider.java | 10 +++++ .../PowerUsageFeatureProviderImpl.java | 10 +++++ .../fuelgauge/BatteryBackupHelperTest.java | 45 ++++++++++++++----- 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java index eb4bbf687d7..5d4f2664050 100644 --- a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java +++ b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; +import android.os.Build; import android.os.IDeviceIdleController; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -36,6 +37,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import java.io.IOException; @@ -56,6 +58,13 @@ public final class BatteryBackupHelper implements BackupHelper { static final String DELIMITER = ","; static final String DELIMITER_MODE = ":"; static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list"; + static final String KEY_BUILD_BRAND = "device_build_brand"; + static final String KEY_BUILD_PRODUCT = "device_build_product"; + static final String KEY_BUILD_MANUFACTURER = "device_build_manufacture"; + static final String KEY_BUILD_FINGERPRINT = "device_build_fingerprint"; + // Customized fields for device extra information. + static final String KEY_BUILD_METADATA_1 = "device_build_metadata_1"; + static final String KEY_BUILD_METADATA_2 = "device_build_metadata_2"; @VisibleForTesting ArraySet mTestApplicationInfoList = null; @@ -83,9 +92,21 @@ public final class BatteryBackupHelper implements BackupHelper { return; } final List allowlistedApps = getFullPowerList(); - if (allowlistedApps != null) { - backupOptimizationMode(data, allowlistedApps); + if (allowlistedApps == null) { + return; } + + writeBackupData(data, KEY_BUILD_BRAND, Build.BRAND); + writeBackupData(data, KEY_BUILD_PRODUCT, Build.PRODUCT); + writeBackupData(data, KEY_BUILD_MANUFACTURER, Build.MANUFACTURER); + writeBackupData(data, KEY_BUILD_FINGERPRINT, Build.FINGERPRINT); + // Add customized device build metadata fields. + PowerUsageFeatureProvider provider = FeatureFactory.getFactory(mContext) + .getPowerUsageFeatureProvider(mContext); + writeBackupData(data, KEY_BUILD_METADATA_1, provider.getBuildMetadata1(mContext)); + writeBackupData(data, KEY_BUILD_METADATA_2, provider.getBuildMetadata2(mContext)); + + backupOptimizationMode(data, allowlistedApps); } @Override @@ -301,6 +322,9 @@ public final class BatteryBackupHelper implements BackupHelper { private static void writeBackupData( BackupDataOutput data, String dataKey, String dataContent) { + if (dataContent == null || dataContent.isEmpty()) { + return; + } final byte[] dataContentBytes = dataContent.getBytes(); try { data.writeEntityHeader(dataKey, dataContentBytes.length); @@ -308,5 +332,6 @@ public final class BatteryBackupHelper implements BackupHelper { } catch (IOException e) { Log.e(TAG, "writeBackupData() is failed for " + dataKey, e); } + Log.d(TAG, String.format("%s:%s", dataKey, dataContent)); } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index 9f30473e209..724ca04d7ae 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -166,4 +166,14 @@ public interface PowerUsageFeatureProvider { * Returns {@link Set} for ignoring task root class names for screen on time */ Set getIgnoreScreenOnTimeTaskRootSet(); + + /** + * Returns the customized device build information for data backup + */ + String getBuildMetadata1(Context context); + + /** + * Returns the customized device build information for data backup + */ + String getBuildMetadata2(Context context); } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index d65c2126658..f73a4d5a256 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -188,4 +188,14 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public Set getIgnoreScreenOnTimeTaskRootSet() { return new ArraySet<>(); } + + @Override + public String getBuildMetadata1(Context context) { + return null; + } + + @Override + public String getBuildMetadata2(Context context) { + return null; + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java index a800fdbd2cc..49354c1c5b5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java @@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; @@ -45,6 +46,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; +import android.os.Build; import android.os.IDeviceIdleController; import android.os.RemoteException; import android.os.UserHandle; @@ -53,6 +55,7 @@ import android.util.ArraySet; import com.android.settings.TestUtils; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import org.junit.After; @@ -89,6 +92,7 @@ public final class BatteryBackupHelperTest { private PrintWriter mPrintWriter; private StringWriter mStringWriter; private BatteryBackupHelper mBatteryBackupHelper; + private PowerUsageFeatureProvider mPowerUsageFeatureProvider; @Mock private PackageManager mPackageManager; @@ -112,6 +116,8 @@ public final class BatteryBackupHelperTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mPowerUsageFeatureProvider = + FakeFeatureFactory.setupForTest().powerUsageFeatureProvider; mContext = spy(RuntimeEnvironment.application); mStringWriter = new StringWriter(); mPrintWriter = new PrintWriter(mStringWriter); @@ -136,19 +142,11 @@ public final class BatteryBackupHelperTest { } @Test - public void performBackup_nullPowerList_notBackupPowerList() throws Exception { - doReturn(null).when(mDeviceController).getFullPowerWhitelist(); - mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null); - - verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); - } - - @Test - public void performBackup_emptyPowerList_notBackupPowerList() throws Exception { + public void performBackup_emptyPowerList_backupPowerList() throws Exception { doReturn(new String[0]).when(mDeviceController).getFullPowerWhitelist(); mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null); - verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); + verify(mBackupDataOutput, atLeastOnce()).writeEntityHeader(anyString(), anyInt()); } @Test @@ -330,6 +328,26 @@ public final class BatteryBackupHelperTest { .setAppUsageState(MODE_RESTRICTED, Action.RESTORE); } + @Test + public void performBackup_backupDeviceBuildInformation() throws Exception { + final String[] fullPowerList = {"com.android.package"}; + doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist(); + doReturn(null).when(mPowerUsageFeatureProvider).getBuildMetadata1(mContext); + final String deviceMetadata = "device.metadata.test_device"; + doReturn(deviceMetadata).when(mPowerUsageFeatureProvider).getBuildMetadata2(mContext); + + mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null); + + final InOrder inOrder = inOrder(mBackupDataOutput); + verifyBackupData(inOrder, BatteryBackupHelper.KEY_BUILD_BRAND, Build.BRAND); + verifyBackupData(inOrder, BatteryBackupHelper.KEY_BUILD_PRODUCT, Build.PRODUCT); + verifyBackupData(inOrder, BatteryBackupHelper.KEY_BUILD_MANUFACTURER, Build.MANUFACTURER); + verifyBackupData(inOrder, BatteryBackupHelper.KEY_BUILD_FINGERPRINT, Build.FINGERPRINT); + inOrder.verify(mBackupDataOutput, never()).writeEntityHeader( + eq(BatteryBackupHelper.KEY_BUILD_METADATA_1), anyInt()); + verifyBackupData(inOrder, BatteryBackupHelper.KEY_BUILD_METADATA_2, deviceMetadata); + } + private void mockUid(int uid, String packageName) throws Exception { doReturn(uid).when(mPackageManager) .getPackageUid(packageName, PackageManager.GET_META_DATA); @@ -401,6 +419,13 @@ public final class BatteryBackupHelperTest { new ArraySet<>(Arrays.asList(applicationInfo1, applicationInfo2, applicationInfo3)); } + private void verifyBackupData( + InOrder inOrder, String dataKey, String dataContent) throws Exception { + final byte[] expectedBytes = dataContent.getBytes(); + inOrder.verify(mBackupDataOutput).writeEntityHeader(dataKey, expectedBytes.length); + inOrder.verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); + } + @Implements(UserHandle.class) public static class ShadowUserHandle { // Sets the default as thte OWNER role. From dad011f5ba038989de998cde8366c1de01942f16 Mon Sep 17 00:00:00 2001 From: Candice Lo Date: Tue, 16 May 2023 02:44:39 +0000 Subject: [PATCH 11/18] Cleaning up Font Size QS tile tooltip Cleaning up Font Size QS tile tooltip in the Settings page in onDestroy to avoid leaking the window. Bug: 279547549 Bug: 279549685 Test: manually check only one tooltip exist Change-Id: Icafa2109af316989330bda5ed831c2f746e7968b --- .../settings/accessibility/PreviewSizeSeekBarController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java index 4c860eb3519..9603739ee6b 100644 --- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java +++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java @@ -28,7 +28,6 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.widget.LabeledSeekBarPreference; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnCreate; import com.android.settingslib.core.lifecycle.events.OnDestroy; @@ -111,6 +110,10 @@ abstract class PreviewSizeSeekBarController extends BasePreferenceController imp public void onDestroy() { // remove runnables in the queue. mHandler.removeCallbacksAndMessages(null); + final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing(); + if (isTooltipWindowShowing) { + mTooltipWindow.dismiss(); + } } @Override From cb92ae1096ebb8a5251245dd771bc03c44ee88d9 Mon Sep 17 00:00:00 2001 From: Candice Lo Date: Tue, 16 May 2023 03:36:00 +0000 Subject: [PATCH 12/18] Cleaning up tooltip for AccessibilityQuickSettingsPrimarySwitchPreferenceController Cleaning up QS tile tooltip in the Settings page in onDestroy to avoid leaking the window. Bug: 279547549 Test: manually check only one tooltip exists Change-Id: I13e94ce549e19fd54c9034f3b3372ecf40388e39 --- ...ibilityQuickSettingsPrimarySwitchPreferenceController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java index 9681a42ad0a..47073638122 100644 --- a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java +++ b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java @@ -66,6 +66,10 @@ public abstract class AccessibilityQuickSettingsPrimarySwitchPreferenceControlle @Override public void onDestroy() { mHandler.removeCallbacksAndMessages(null); + final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing(); + if (isTooltipWindowShowing) { + mTooltipWindow.dismiss(); + } } @Override From ceebc93e895f4b6f8459d574f277d31549ef7835 Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Mon, 15 May 2023 12:00:57 +0000 Subject: [PATCH 13/18] [Regional Preference] Should not save "default" into Locale. Set wrong value in the previous refactory changes, should set valus as null when the default is selected Bug: 281993323 Test: manual test and QA test Change-Id: I897791a38a50588d7d721bf57c0d7bdc1734e189 --- .../RegionalPreferenceListBasePreferenceController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java index 823f5bbc5c3..1e39ffff5e7 100644 --- a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java +++ b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java @@ -63,7 +63,9 @@ public abstract class RegionalPreferenceListBasePreferenceController extends pref.setKey(item); pref.setOnPreferenceClickListener(clickedPref -> { setSelected(pref); - RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(), item); + RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(), + item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE) + ? null : item); mMetricsFeatureProvider.action(mContext, getMetricsActionKey()); return true; }); From 3dab6548b9bca1db34baff7289ff1ef751d293c7 Mon Sep 17 00:00:00 2001 From: Candice Lo Date: Tue, 16 May 2023 06:27:44 +0000 Subject: [PATCH 14/18] Cleaning up tooltip for ToggleFeaturePreferenceFragment Cleaning up QS tile tooltip in the Settings page in onDestroy to avoid leaking the window. Bug: 279547549 Test: manually check only one tooltip exists Change-Id: I781cdba1be7be0315de12cd57620521b40969aee --- .../accessibility/ToggleFeaturePreferenceFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index edbd120e698..6a4344ff0d2 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -296,6 +296,10 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment public void onDestroyView() { super.onDestroyView(); removeActionBarToggleSwitch(); + final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing(); + if (isTooltipWindowShowing) { + mTooltipWindow.dismiss(); + } } @Override From cc1b643a79266fa59dcf17916ce808358f3cccc9 Mon Sep 17 00:00:00 2001 From: Avinash Vadlamudi Date: Mon, 15 May 2023 13:58:23 +0000 Subject: [PATCH 15/18] [Auto Pin Confirmation]: Increase the touch target of checkbox to 48dp for accessibility Bug: 281945287 Test: Manual test by flashing on device and verifying using layout bounds Change-Id: Ibfbb3f3e7e8307c9ec5c01226c24da10fb5e080a --- res/drawable/checkbox_circle_shape.xml | 49 +++++++++++++++----------- res/layout/choose_lock_password.xml | 1 - 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/res/drawable/checkbox_circle_shape.xml b/res/drawable/checkbox_circle_shape.xml index 68c5a2ed359..51a567c220f 100644 --- a/res/drawable/checkbox_circle_shape.xml +++ b/res/drawable/checkbox_circle_shape.xml @@ -1,24 +1,31 @@ + 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. +--> + - - \ No newline at end of file + android:bottom="12dp" + android:left="12dp" + android:right="12dp" + android:top="12dp"> + + + + + + \ No newline at end of file diff --git a/res/layout/choose_lock_password.xml b/res/layout/choose_lock_password.xml index b748f94e80a..5819774a75e 100644 --- a/res/layout/choose_lock_password.xml +++ b/res/layout/choose_lock_password.xml @@ -74,7 +74,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center" - android:paddingLeft="14dp" android:text="@string/auto_pin_confirm_user_message" android:textSize="16sp" android:button="@drawable/checkbox_circle_shape" From 41eeb31b200fa6764432378d974c01c3a17d843a Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 16 May 2023 18:02:17 +0800 Subject: [PATCH 16/18] Update theme for ChannelPanelActivity - set correct switch style - update background color Fixes: 238845212 Test: visual Change-Id: Iaf54fa5d98f7527543fc02d1108cbafe851bd574 --- AndroidManifest.xml | 2 +- res/drawable/button_border_selected.xml | 3 --- res/drawable/button_border_unselected.xml | 2 +- res/values/themes.xml | 3 +++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6fe6832b76a..08fbf04441d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3717,7 +3717,7 @@ diff --git a/res/drawable/button_border_selected.xml b/res/drawable/button_border_selected.xml index 0cd4aa54b5a..1402380d0ed 100644 --- a/res/drawable/button_border_selected.xml +++ b/res/drawable/button_border_selected.xml @@ -15,10 +15,7 @@ limitations under the License. --> - diff --git a/res/drawable/button_border_unselected.xml b/res/drawable/button_border_unselected.xml index 2c2ef3d8d83..d0ce75b8c86 100644 --- a/res/drawable/button_border_unselected.xml +++ b/res/drawable/button_border_unselected.xml @@ -18,7 +18,7 @@ android:shape="rectangle"> + android:color="?android:attr/colorAccent"/> diff --git a/res/values/themes.xml b/res/values/themes.xml index eeba1c77aca..8f132799b79 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -227,6 +227,9 @@ @*android:string/config_headlineFontFamilyMedium +