Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb136dd8 authored by Kuan Wang's avatar Kuan Wang
Browse files

Copy BatteryUsageLoaderService and BatteryBroadcastReceiver from

SettingsGoogle to Settings and rename them to BatteryUsageDataLoader and
BatteryUsageBroadcastReceiver.

Bug: 253395332
Test: make RunSettingsRoboTests
Change-Id: Ide7c572a7df826ca576223c297b8ec78c45cc94e
parent 772c437d
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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"
+62 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+78 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+106 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+165 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}