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);
+ }
+}