diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java index 5e432cf5b4e..ecc4ea0692b 100644 --- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.usb.UsbManager; import android.os.BatteryManager; import android.os.PowerManager; import android.util.Log; @@ -99,6 +100,7 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); intentFilter.addAction(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION); + intentFilter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED); final Intent intent = mContext.registerReceiver(this, intentFilter, Context.RECEIVER_EXPORTED); @@ -110,33 +112,36 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { } private void updateBatteryStatus(Intent intent, boolean forceUpdate) { - if (intent != null && mBatteryListener != null) { - if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - final String batteryLevel = Utils.getBatteryPercentage(intent); - final String batteryStatus = - Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false); - final int batteryHealth = intent.getIntExtra( - BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN); - 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 (batteryHealth != mBatteryHealth) { - mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH); - } else if(!batteryLevel.equals(mBatteryLevel)) { - mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL); - } else if (!batteryStatus.equals(mBatteryStatus)) { - mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); - } - mBatteryLevel = batteryLevel; - mBatteryStatus = batteryStatus; - mBatteryHealth = batteryHealth; - } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER); - } else if (BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION.equals(intent.getAction())) { + if (intent == null || mBatteryListener == null) { + return; + } + final String action = intent.getAction(); + if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { + final String batteryLevel = Utils.getBatteryPercentage(intent); + final String batteryStatus = + Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false); + final int batteryHealth = intent.getIntExtra( + BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN); + 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 (batteryHealth != mBatteryHealth) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH); + } else if(!batteryLevel.equals(mBatteryLevel)) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL); + } else if (!batteryStatus.equals(mBatteryStatus)) { mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); } + mBatteryLevel = batteryLevel; + mBatteryStatus = batteryStatus; + mBatteryHealth = batteryHealth; + } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER); + } else if (BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION.equals(action) + || UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED.equals(action)) { + mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); } } } diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java index da646cbff89..437ffdad280 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java @@ -26,6 +26,7 @@ import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector; import com.android.settings.fuelgauge.batterytip.detectors.DockDefenderDetector; import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector; +import com.android.settings.fuelgauge.batterytip.detectors.IncompatibleChargerDetector; import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector; import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; @@ -59,15 +60,15 @@ public class BatteryTipLoader extends AsyncLoaderCompat> { final List tips = new ArrayList<>(); final BatteryTipPolicy policy = new BatteryTipPolicy(getContext()); final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(TAG); - final Context context = getContext(); + final Context context = getContext().getApplicationContext(); tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect()); tips.add(new HighUsageDetector(context, policy, mBatteryUsageStats, batteryInfo).detect()); tips.add(new SmartBatteryDetector( context, policy, batteryInfo, context.getContentResolver()).detect()); - tips.add(new BatteryDefenderDetector( - batteryInfo, context.getApplicationContext()).detect()); - tips.add(new DockDefenderDetector(batteryInfo, context.getApplicationContext()).detect()); + tips.add(new BatteryDefenderDetector(batteryInfo, context).detect()); + tips.add(new DockDefenderDetector(batteryInfo, context).detect()); + tips.add(new IncompatibleChargerDetector(context, batteryInfo).detect()); Collections.sort(tips); return tips; } diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java new file mode 100644 index 00000000000..483e37ddcaa --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.fuelgauge.batterytip.detectors; + +import android.content.Context; +import android.util.Log; + +import com.android.settings.fuelgauge.BatteryInfo; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; +import com.android.settings.fuelgauge.batterytip.tips.IncompatibleChargerTip; +import com.android.settingslib.Utils; + +/** Detect whether it is in the incompatible charging state */ +public final class IncompatibleChargerDetector implements BatteryTipDetector { + private static final String TAG = "IncompatibleChargerDetector"; + + private final Context mContext; + private final BatteryInfo mBatteryInfo; + + public IncompatibleChargerDetector(Context context, BatteryInfo batteryInfo) { + mContext = context; + mBatteryInfo = batteryInfo; + } + + @Override + public BatteryTip detect() { + int state = BatteryTip.StateType.INVISIBLE; + boolean isIncompatibleCharging = false; + + // Check incompatible charging state if the device is plugged. + if (mBatteryInfo.pluggedStatus != 0) { + isIncompatibleCharging = Utils.containsIncompatibleChargers(mContext, TAG); + if (isIncompatibleCharging) { + state = BatteryTip.StateType.NEW; + } + } + Log.d(TAG, "detect() state= " + state + " isIncompatibleCharging: " + + isIncompatibleCharging); + return new IncompatibleChargerTip(state); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java index 4bfb15baf4e..a829c40e9a6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -29,6 +30,8 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbManager; import android.os.BatteryManager; import android.os.PowerManager; @@ -37,6 +40,7 @@ import com.android.settings.Utils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -55,7 +59,6 @@ public class BatteryBroadcastReceiverTest { private BatteryBroadcastReceiver mBatteryBroadcastReceiver; private Context mContext; private Intent mChargingIntent; - private Intent mDockDefenderBypassIntent; @Before public void setUp() { @@ -73,12 +76,10 @@ public class BatteryBroadcastReceiverTest { mChargingIntent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_INTENT_SCALE); mChargingIntent .putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); - mDockDefenderBypassIntent = new Intent(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION); - } @Test - public void testOnReceive_batteryLevelChanged_dataUpdated() { + public void onReceive_batteryLevelChanged_dataUpdated() { mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent); assertThat(mBatteryBroadcastReceiver.mBatteryLevel) @@ -89,7 +90,7 @@ public class BatteryBroadcastReceiverTest { } @Test - public void testOnReceive_batteryHealthChanged_dataUpdated() { + public void onReceive_batteryHealthChanged_dataUpdated() { mChargingIntent .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT); mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent); @@ -109,7 +110,7 @@ public class BatteryBroadcastReceiverTest { } @Test - public void testOnReceive_powerSaveModeChanged_listenerInvoked() { + public void onReceive_powerSaveModeChanged_listenerInvoked() { mBatteryBroadcastReceiver.onReceive(mContext, new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); @@ -117,7 +118,7 @@ public class BatteryBroadcastReceiverTest { } @Test - public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() { + public void onReceive_batteryDataNotChanged_listenerNotInvoked() { final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent); final String batteryStatus = Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false); @@ -134,14 +135,23 @@ public class BatteryBroadcastReceiverTest { } @Test - public void testOnReceive_dockDefenderBypassed_listenerInvoked() { - mBatteryBroadcastReceiver.onReceive(mContext, mDockDefenderBypassIntent); + public void onReceive_dockDefenderBypassed_listenerInvoked() { + mBatteryBroadcastReceiver.onReceive(mContext, + new Intent(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION)); verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); } @Test - public void testRegister_updateBatteryStatus() { + public void onReceive_usbPortComplianceChanged_listenerInvoked() { + mBatteryBroadcastReceiver.onReceive(mContext, + new Intent(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED)); + + verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); + } + + @Test + public void register_updateBatteryStatus() { doReturn(mChargingIntent).when(mContext).registerReceiver(any(), any(), anyInt()); mBatteryBroadcastReceiver.register(); @@ -156,4 +166,23 @@ public class BatteryBroadcastReceiverTest { // 2 times because register will force update the battery verify(mBatteryListener, times(2)).onBatteryChanged(BatteryUpdateType.MANUAL); } + + @Test + public void register_registerExpectedIntent() { + mBatteryBroadcastReceiver.register(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(IntentFilter.class); + verify(mContext).registerReceiver( + eq(mBatteryBroadcastReceiver), + captor.capture(), + eq(Context.RECEIVER_EXPORTED)); + assertAction(captor, Intent.ACTION_BATTERY_CHANGED); + assertAction(captor, PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + assertAction(captor, BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION); + assertAction(captor, UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED); + } + + private void assertAction(ArgumentCaptor captor, String action) { + assertThat(captor.getValue().hasAction(action)).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java index 0e1c4585969..5b0ae046c5b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java @@ -53,6 +53,7 @@ public class BatteryTipLoaderTest { BatteryTip.TipType.LOW_BATTERY, BatteryTip.TipType.BATTERY_DEFENDER, BatteryTip.TipType.DOCK_DEFENDER, + BatteryTip.TipType.INCOMPATIBLE_CHARGER, BatteryTip.TipType.HIGH_DEVICE_USAGE, BatteryTip.TipType.SMART_BATTERY_MANAGER}; @Mock(answer = Answers.RETURNS_DEEP_STUBS) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java new file mode 100644 index 00000000000..1dfe6e270c6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.fuelgauge.batterytip.detectors; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.usb.UsbManager; +import android.hardware.usb.UsbPort; +import android.hardware.usb.UsbPortStatus; + +import com.android.settings.fuelgauge.BatteryInfo; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public final class IncompatibleChargerDetectorTest { + + @Mock private BatteryInfo mBatteryInfo; + @Mock private UsbPort mUsbPort; + @Mock private UsbManager mUsbManager; + @Mock private UsbPortStatus mUsbPortStatus; + + private Context mContext; + private IncompatibleChargerDetector mIncompatibleChargerDetector; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager); + mIncompatibleChargerDetector = + new IncompatibleChargerDetector(mContext, mBatteryInfo); + } + + @Test + public void detect_unplugDevice_shouldNotShowTip() { + mBatteryInfo.pluggedStatus = 0; + + BatteryTip batteryTip = mIncompatibleChargerDetector.detect(); + + assertThat(batteryTip.isVisible()).isFalse(); + assertThat(batteryTip.getState()).isEqualTo(BatteryTip.StateType.INVISIBLE); + } + + @Test + public void detect_plugDeviceWithoutIncompatibleCharger_shouldNotShowTip() { + mBatteryInfo.pluggedStatus = 1; + + BatteryTip batteryTip = mIncompatibleChargerDetector.detect(); + + assertThat(batteryTip.isVisible()).isFalse(); + assertThat(batteryTip.getState()).isEqualTo(BatteryTip.StateType.INVISIBLE); + } + + @Test + public void detect_plugDeviceWithIncompatibleCharger_showTip() { + mBatteryInfo.pluggedStatus = 1; + setupIncompatibleCharging(); + + BatteryTip batteryTip = mIncompatibleChargerDetector.detect(); + + assertThat(batteryTip.isVisible()).isTrue(); + assertThat(batteryTip.getState()).isEqualTo(BatteryTip.StateType.NEW); + } + + private void setupIncompatibleCharging() { + final List usbPorts = new ArrayList<>(); + usbPorts.add(mUsbPort); + when(mUsbManager.getPorts()).thenReturn(usbPorts); + when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus); + when(mUsbPort.supportsComplianceWarnings()).thenReturn(true); + when(mUsbPortStatus.isConnected()).thenReturn(true); + when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1}); + } +}