Use battery unplugging event to compute the full charge start time on
Pixel devices.

Test: make RunSettingsRoboTests + manual
Bug: 256124406
Change-Id: I80b33db6e25ac0c693c50ddf93b6343c0fea942f
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eed87ed..0fb6dc8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3051,11 +3051,12 @@
             android:exported="false"
             android:authorities="${applicationId}.battery.usage.bugreport"/>
 
-        <receiver android:name=".fuelgauge.batteryusage.BatteryUsageBroadcastReceiver"
+        <receiver android:name="com.android.settings.fuelgauge.batteryusage.BatteryUsageBroadcastReceiver"
                   android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
                 <action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
+                <action android:name="com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"/>
             </intent-filter>
         </receiver>
 
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index c151517..66e09a9 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -133,11 +133,16 @@
     boolean delayHourlyJobWhenBooting();
 
     /**
-     * Gets a intent for one time bypass charge limited to resume charging.
+     * Gets an intent for one time bypass charge limited to resume charging.
      */
     Intent getResumeChargeIntent(boolean isDockDefender);
 
     /**
+     * Returns the intent action used to mark as the full charge start event.
+     */
+    String getFullChargeIntentAction();
+
+    /**
      * Returns {@link Set} for the system component ids which are combined into others
      */
     Set<Integer> getOthersSystemComponentSet();
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 657e5c6..e0af88a 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -148,6 +148,11 @@
     }
 
     @Override
+    public String getFullChargeIntentAction() {
+        return Intent.ACTION_BATTERY_LEVEL_CHANGED;
+    }
+
+    @Override
     public boolean isExtraDefend() {
         return false;
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
index 817d367..9c7ec35 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 
 import java.time.Duration;
@@ -36,6 +37,9 @@
     /** An intent action to request Settings to clear cache data. */
     public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
             "com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
+    /** An intent action to request Settings to clear cache data. */
+    public static final String ACTION_BATTERY_UNPLUGGING =
+            "com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
 
     @VisibleForTesting
     static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
@@ -51,9 +55,27 @@
             return;
         }
         Log.d(TAG, "onReceive:" + intent.getAction());
+        final String fullChargeIntentAction = FeatureFactory.getFactory(context)
+                .getPowerUsageFeatureProvider(context)
+                .getFullChargeIntentAction();
         switch (intent.getAction()) {
             case Intent.ACTION_BATTERY_LEVEL_CHANGED:
-                tryToFetchUsageData(context);
+                // Only when fullChargeIntentAction is ACTION_BATTERY_LEVEL_CHANGED,
+                // ACTION_BATTERY_LEVEL_CHANGED will be considered as the full charge event and then
+                // start usage events fetching.
+                if (Intent.ACTION_BATTERY_LEVEL_CHANGED.equals(fullChargeIntentAction)) {
+                    Log.d(TAG, "fetch data because of event: ACTION_BATTERY_LEVEL_CHANGED");
+                    tryToFetchUsageData(context);
+                }
+                break;
+            case ACTION_BATTERY_UNPLUGGING:
+                // Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED,
+                // ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then
+                // start usage events fetching.
+                if (Intent.ACTION_POWER_DISCONNECTED.equals(fullChargeIntentAction)) {
+                    Log.d(TAG, "fetch data because of event: ACTION_POWER_DISCONNECTED");
+                    tryToFetchUsageData(context);
+                }
                 break;
             case ACTION_CLEAR_BATTERY_CACHE_DATA:
                 if (sIsDebugMode) {
@@ -74,7 +96,7 @@
         final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime();
         // If current boot time is smaller than expected delay, cancel sending the broadcast.
         if (broadcastDelay > 0) {
-            Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is"
+            Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is "
                     + broadcastDelay + "ms.");
             return;
         }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
index addfd9b..6048197 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.Intent;
@@ -29,6 +30,8 @@
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 
+import com.android.settings.testutils.FakeFeatureFactory;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,6 +45,8 @@
 
     private Context mContext;
     private BatteryUsageBroadcastReceiver mBatteryUsageBroadcastReceiver;
+    private FakeFeatureFactory mFakeFeatureFactory;
+
     @Mock
     private PackageManager mPackageManager;
 
@@ -49,6 +54,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
         mBatteryUsageBroadcastReceiver = new BatteryUsageBroadcastReceiver();
         doReturn(mPackageManager).when(mContext).getPackageManager();
     }
@@ -62,6 +68,8 @@
 
     @Test
     public void onReceive_actionBatteryLevelChanged_notFetchUsageData_notFullCharged() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED);
         doReturn(getBatteryIntent(/*level=*/ 20, BatteryManager.BATTERY_STATUS_UNKNOWN))
                 .when(mContext).registerReceiver(any(), any());
 
@@ -72,7 +80,9 @@
     }
 
     @Test
-    public void onReceive_actionBatteryLevelChanged_cancelFetchUsageData() {
+    public void onReceive_actionBatteryLevelChanged_notFetchUsageData_nearBooting() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED);
         // Make sure isCharged returns true.
         doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_FULL))
                 .when(mContext).registerReceiver(any(), any());
@@ -87,7 +97,25 @@
     }
 
     @Test
-    public void onReceive_actionBatteryLevelChanged_notFetchUsageData() {
+    public void onReceive_actionBatteryLevelChanged_notFetchUsageData_wrongAction() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_POWER_DISCONNECTED);
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryLevelChanged_fetchUsageData() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED);
         // Make sure isCharged returns true.
         doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN))
                 .when(mContext).registerReceiver(any(), any());
@@ -100,6 +128,67 @@
         assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue();
     }
 
+    @Test
+    public void onReceive_actionBatteryUnplugging_notFetchUsageData_notFullCharged() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_POWER_DISCONNECTED);
+        doReturn(getBatteryIntent(/*level=*/ 20, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryUnplugging_notFetchUsageData_nearBooting() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_POWER_DISCONNECTED);
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_FULL))
+                .when(mContext).registerReceiver(any(), any());
+        // Make sure broadcast will be sent with delay.
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() + 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryUnplugging_notFetchUsageData_wrongAction() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_actionBatteryUnplugging_fetchUsageData() {
+        when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
+                .thenReturn(Intent.ACTION_POWER_DISCONNECTED);
+        // Make sure isCharged returns true.
+        doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN))
+                .when(mContext).registerReceiver(any(), any());
+        BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot =
+                SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS;
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue();
+    }
 
     @Test
     public void onReceive_clearCacheIntentInDebugMode_clearBatteryCacheData() {