Copy BatteryUsageLoaderService and BatteryBroadcastReceiver from
SettingsGoogle to Settings and rename them to BatteryUsageDataLoader and
BatteryUsageBroadcastReceiver.

Bug: 253395332
Test: make RunSettingsRoboTests
Change-Id: Ide7c572a7df826ca576223c297b8ec78c45cc94e
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 935c1a4..a3a6fcf 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2973,6 +2973,14 @@
             android:authorities="${applicationId}.battery.usage.provider"
             android:permission="com.android.settings.BATTERY_DATA"/>
 
+        <receiver android:name=".fuelgauge.batteryusage.BatteryUsageBroadcastReceiver"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.settings.battery.action.FETCH_BATTERY_USAGE_DATA"/>
+                <action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
+            </intent-filter>
+        </receiver>
+
         <activity
             android:name="Settings$BatterySaverSettingsActivity"
             android:label="@string/battery_saver"
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
new file mode 100644
index 0000000..5d8757d
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+/** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
+public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = "BatteryUsageBroadcastReceiver";
+    /** An intent action to request Settings to fetch usage data. */
+    public static final String ACTION_FETCH_BATTERY_USAGE_DATA =
+            "com.android.settings.battery.action.FETCH_BATTERY_USAGE_DATA";
+    /** 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";
+
+    @VisibleForTesting
+    static boolean sIsDebugMode = Build.TYPE.equals("userdebug");
+
+    @VisibleForTesting
+    boolean mFetchBatteryUsageData = false;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent == null || intent.getAction() == null) {
+            return;
+        }
+        Log.d(TAG, "onReceive:" + intent.getAction());
+        switch (intent.getAction()) {
+            case ACTION_FETCH_BATTERY_USAGE_DATA:
+                mFetchBatteryUsageData = true;
+                BatteryUsageDataLoader.enqueueWork(context);
+                break;
+            case ACTION_CLEAR_BATTERY_CACHE_DATA:
+                if (sIsDebugMode) {
+                    BatteryDiffEntry.clearCache();
+                    BatteryEntry.clearUidCache();
+                }
+                break;
+        }
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
new file mode 100644
index 0000000..dc9c8b7
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -0,0 +1,78 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.BatteryUsageStats;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+
+/** Load battery usage data in the background. */
+public final class BatteryUsageDataLoader {
+    private static final String TAG = "BatteryUsageDataLoader";
+
+    @VisibleForTesting
+    static BatteryAppListPreferenceController sController;
+
+    private BatteryUsageDataLoader() {
+    }
+
+    static void enqueueWork(Context context) {
+        AsyncTask.execute(() -> {
+            Log.d(TAG, "loadUsageDataSafely() in the AsyncTask");
+            loadUsageDataSafely(context.getApplicationContext());
+        });
+    }
+
+    @VisibleForTesting
+    static void loadUsageData(Context context) {
+        // Checks whether the battery content provider is available.
+        if (!DatabaseUtils.isContentProviderEnabled(context)) {
+            Log.w(TAG, "battery usage content provider is disabled!");
+            return;
+        }
+        final long start = System.currentTimeMillis();
+        final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
+        final List<BatteryEntry> batteryEntryList =
+                DataProcessor.generateBatteryEntryListFromBatteryUsageStats(
+                        context,
+                        batteryUsageStats,
+                        sController);
+        if (batteryEntryList == null || batteryEntryList.isEmpty()) {
+            Log.w(TAG, "getBatteryEntryList() returns null or empty content");
+        }
+        final long elapsedTime = System.currentTimeMillis() - start;
+        Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime));
+
+        // Uploads the BatteryEntry data into SettingsIntelligence.
+        DatabaseUtils.sendBatteryEntryData(
+                context, batteryEntryList, batteryUsageStats);
+        DataProcessor.closeBatteryUsageStats(batteryUsageStats);
+    }
+
+    private static void loadUsageDataSafely(Context context) {
+        try {
+            loadUsageData(context);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "loadUsageData:" + e);
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
new file mode 100644
index 0000000..c7e8322
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryUsageBroadcastReceiverTest {
+
+    private Context mContext;
+    private BatteryUsageBroadcastReceiver mBatteryUsageBroadcastReceiver;
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mBatteryUsageBroadcastReceiver = new BatteryUsageBroadcastReceiver();
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+    }
+
+    @Test
+    public void onReceive_fetchUsageDataIntent_startService() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_FETCH_BATTERY_USAGE_DATA));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue();
+    }
+
+    @Test
+    public void onReceive_invalidIntent_notStartService() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        mBatteryUsageBroadcastReceiver.onReceive(mContext, new Intent("invalid intent"));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
+    public void onReceive_clearCacheIntentInDebugMode_clearBatteryCacheData() {
+        BatteryUsageBroadcastReceiver.sIsDebugMode = true;
+        // Insert testing data first.
+        BatteryDiffEntry.sValidForRestriction.put(
+                /*packageName*/ "com.android.testing_package", Boolean.valueOf(true));
+        assertThat(BatteryDiffEntry.sValidForRestriction).isNotEmpty();
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_CLEAR_BATTERY_CACHE_DATA));
+
+        assertThat(BatteryDiffEntry.sValidForRestriction).isEmpty();
+    }
+
+    @Test
+    public void onReceive_clearCacheIntentInNotDebugMode_notClearBatteryCacheData() {
+        BatteryUsageBroadcastReceiver.sIsDebugMode = false;
+        // Insert testing data first.
+        BatteryDiffEntry.sValidForRestriction.put(
+                /*packageName*/ "com.android.testing_package", Boolean.valueOf(true));
+        assertThat(BatteryDiffEntry.sValidForRestriction).isNotEmpty();
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_CLEAR_BATTERY_CACHE_DATA));
+
+        assertThat(BatteryDiffEntry.sValidForRestriction).isNotEmpty();
+    }
+
+    private void setProviderSetting(int value) {
+        when(mPackageManager.getComponentEnabledSetting(
+                new ComponentName(
+                        DatabaseUtils.SETTINGS_PACKAGE_PATH,
+                        DatabaseUtils.BATTERY_PROVIDER_CLASS_PATH)))
+                .thenReturn(value);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoaderTest.java
new file mode 100644
index 0000000..4124e34
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoaderTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+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 BatteryUsageDataLoaderTest {
+
+    private Context mContext;
+    @Mock
+    private ContentResolver mMockContentResolver;
+    @Mock
+    private BatteryStatsManager mBatteryStatsManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private BatteryUsageStats mBatteryUsageStats;
+    @Mock
+    private BatteryAppListPreferenceController mMockBatteryAppListController;
+    @Mock
+    private BatteryEntry mMockBatteryEntry;
+    @Captor
+    private ArgumentCaptor<BatteryUsageStatsQuery> mStatsQueryCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        BatteryUsageDataLoader.sController = mMockBatteryAppListController;
+        doReturn(mContext).when(mContext).getApplicationContext();
+        doReturn(mBatteryStatsManager).when(mContext).getSystemService(
+                Context.BATTERY_STATS_SERVICE);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mMockContentResolver).when(mContext).getContentResolver();
+        doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
+    }
+
+    @Test
+    public void loadUsageData_loadUsageDataWithHistory() {
+        final List<BatteryEntry> batteryEntryList = new ArrayList<>();
+        batteryEntryList.add(mMockBatteryEntry);
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
+                .thenReturn(mBatteryUsageStats);
+        when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
+                .thenReturn(batteryEntryList);
+
+        BatteryUsageDataLoader.loadUsageData(mContext);
+
+        final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
+        assertThat(queryFlags
+                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY)
+                .isNotEqualTo(0);
+        verify(mMockBatteryAppListController)
+                .getBatteryEntryList(mBatteryUsageStats, /*showAllApps=*/ true);
+        verify(mMockContentResolver).insert(any(), any());
+    }
+
+    @Test
+    public void loadUsageData_nullBatteryUsageStats_notLoadBatteryEntryData() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
+                .thenReturn(null);
+
+        BatteryUsageDataLoader.loadUsageData(mContext);
+
+        final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
+        assertThat(queryFlags
+                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY)
+                .isNotEqualTo(0);
+        verify(mMockBatteryAppListController, never())
+                .getBatteryEntryList(mBatteryUsageStats, /*showAllApps=*/ true);
+        verify(mMockContentResolver).insert(any(), any());
+    }
+
+    @Test
+    public void loadUsageData_nullBatteryEntryList_insertFakeDataIntoProvider() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
+                .thenReturn(mBatteryUsageStats);
+        when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
+                .thenReturn(null);
+
+        BatteryUsageDataLoader.loadUsageData(mContext);
+
+        verify(mMockContentResolver).insert(any(), any());
+    }
+
+    @Test
+    public void loadUsageData_emptyBatteryEntryList_insertFakeDataIntoProvider() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
+                .thenReturn(mBatteryUsageStats);
+        when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
+                .thenReturn(new ArrayList<BatteryEntry>());
+
+        BatteryUsageDataLoader.loadUsageData(mContext);
+
+        verify(mMockContentResolver).insert(any(), any());
+    }
+
+    @Test
+    public void loadUsageData_providerIsDisabled_notLoadHistory() {
+        setProviderSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
+                .thenReturn(mBatteryUsageStats);
+
+        BatteryUsageDataLoader.loadUsageData(mContext);
+
+        verify(mBatteryStatsManager, never()).getBatteryUsageStats(
+                mStatsQueryCaptor.capture());
+    }
+
+    private void setProviderSetting(int value) {
+        when(mPackageManager.getComponentEnabledSetting(
+                new ComponentName(
+                        DatabaseUtils.SETTINGS_PACKAGE_PATH,
+                        DatabaseUtils.BATTERY_PROVIDER_CLASS_PATH)))
+                .thenReturn(value);
+    }
+}