diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 935c1a47f0d..a3a6fcf64a1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2973,6 +2973,14 @@ android:authorities="${applicationId}.battery.usage.provider" android:permission="com.android.settings.BATTERY_DATA"/> + + + + + + + { + 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 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 00000000000..c7e83228b9f --- /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 00000000000..4124e34c242 --- /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 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 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()); + + 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); + } +}