From c5059b7963121f46ce0533180bdc1c42a35b787e Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Thu, 10 Dec 2020 16:10:21 -0800 Subject: Rename powerstatstests to batterystatstests Bug: 170134969 Test: mp :BatteryStatsViewer && adb shell am start -n com.android.frameworks.core.batterystatsviewer/.BatteryStatsViewerActivity Test: atest BatteryStatsLoadTests Change-Id: I7bcd24fd89542b854f8b037bb7d8fc184bea9853 --- .../BatteryStatsLoadTests/Android.bp | 13 + .../BatteryStatsLoadTests/AndroidManifest.xml | 37 +++ .../ConnectivitySetupRule.java | 159 ++++++++++ .../core/batterystatsloadtests/PowerMetrics.java | 278 +++++++++++++++++ .../PowerMetricsCollector.java | 305 +++++++++++++++++++ .../SystemServiceCallLoadTest.java | 69 +++++ .../core/batterystatsloadtests/WiFiLoadTest.java | 72 +++++ .../BatteryStatsViewer/Android.bp | 13 + .../BatteryStatsViewer/AndroidManifest.xml | 41 +++ .../res/layout/battery_consumer_entry_layout.xml | 49 +++ .../res/layout/battery_consumer_info_layout.xml | 85 ++++++ .../battery_consumer_picker_activity_layout.xml | 33 ++ .../res/layout/battery_consumer_picker_layout.xml | 35 +++ .../res/layout/battery_stats_viewer_layout.xml | 77 +++++ .../BatteryStatsViewer/res/values/styles.xml | 34 +++ .../batterystatsviewer/BatteryConsumerData.java | 302 ++++++++++++++++++ .../BatteryConsumerInfoHelper.java | 156 ++++++++++ .../BatteryConsumerPickerActivity.java | 116 +++++++ .../BatteryConsumerPickerFragment.java | 257 ++++++++++++++++ .../BatteryStatsViewerActivity.java | 338 +++++++++++++++++++++ core/tests/batterystatstests/OWNERS | 2 + core/tests/powertests/OWNERS | 2 - .../powertests/PowerStatsLoadTests/Android.bp | 13 - .../PowerStatsLoadTests/AndroidManifest.xml | 37 --- .../powerstatsloadtests/ConnectivitySetupRule.java | 159 ---------- .../core/powerstatsloadtests/PowerMetrics.java | 278 ----------------- .../powerstatsloadtests/PowerMetricsCollector.java | 304 ------------------ .../SystemServiceCallLoadTest.java | 69 ----- .../core/powerstatsloadtests/WiFiLoadTest.java | 72 ----- core/tests/powertests/PowerStatsViewer/Android.bp | 13 - .../PowerStatsViewer/AndroidManifest.xml | 41 --- .../res/layout/power_consumer_info_layout.xml | 85 ------ .../power_consumer_picker_activity_layout.xml | 33 -- .../res/layout/power_consumer_picker_layout.xml | 35 --- .../res/layout/power_stats_entry_layout.xml | 49 --- .../res/layout/power_stats_viewer_layout.xml | 77 ----- .../PowerStatsViewer/res/values/styles.xml | 34 --- .../powerstatsviewer/PowerConsumerInfoHelper.java | 156 ---------- .../PowerConsumerPickerActivity.java | 116 ------- .../PowerConsumerPickerFragment.java | 256 ---------------- .../core/powerstatsviewer/PowerStatsData.java | 303 ------------------ .../powerstatsviewer/PowerStatsViewerActivity.java | 336 -------------------- 42 files changed, 2471 insertions(+), 2468 deletions(-) create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/AndroidManifest.xml create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/ConnectivitySetupRule.java create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/SystemServiceCallLoadTest.java create mode 100644 core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/WiFiLoadTest.java create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/Android.bp create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_info_layout.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_activity_layout.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java create mode 100644 core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java create mode 100644 core/tests/batterystatstests/OWNERS delete mode 100644 core/tests/powertests/OWNERS delete mode 100644 core/tests/powertests/PowerStatsLoadTests/Android.bp delete mode 100644 core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml delete mode 100644 core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java delete mode 100644 core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java delete mode 100644 core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java delete mode 100644 core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java delete mode 100644 core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java delete mode 100644 core/tests/powertests/PowerStatsViewer/Android.bp delete mode 100644 core/tests/powertests/PowerStatsViewer/AndroidManifest.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/res/values/styles.xml delete mode 100644 core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java delete mode 100644 core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java delete mode 100644 core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java delete mode 100644 core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java delete mode 100644 core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp new file mode 100644 index 000000000000..ade97b81e775 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp @@ -0,0 +1,13 @@ +android_test { + name: "BatteryStatsLoadTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "androidx.test.ext.junit", + "compatibility-device-util-axt", + "junit", + ], + libs: ["android.test.runner"], + platform_apis: true, + certificate: "platform", +} diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsLoadTests/AndroidManifest.xml new file mode 100644 index 000000000000..adc20c6ae496 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/ConnectivitySetupRule.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/ConnectivitySetupRule.java new file mode 100644 index 000000000000..1afc22b7406d --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/ConnectivitySetupRule.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsloadtests; + +import static org.junit.Assert.assertEquals; + +import android.app.Instrumentation; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.SystemUtil; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ConnectivitySetupRule implements TestRule { + + private final boolean mWifiEnabled; + private final ConnectivityManager mConnectivityManager; + private final WifiManager mWifiManager; + private boolean mInitialWifiState; + + public ConnectivitySetupRule(boolean wifiEnabled) { + mWifiEnabled = wifiEnabled; + + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + Context context = instrumentation.getContext(); + mConnectivityManager = context.getSystemService(ConnectivityManager.class); + mWifiManager = context.getSystemService(WifiManager.class); + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + mInitialWifiState = isWiFiConnected(); + setWiFiState(mWifiEnabled); + base.evaluate(); + } finally { + setWiFiState(mInitialWifiState); + } + } + }; + } + + private void setWiFiState(final boolean enable) throws InterruptedException { + boolean wiFiConnected = isWiFiConnected(); + if (enable == wiFiConnected) { + return; + } + + NetworkTracker tracker = new NetworkTracker(!mWifiEnabled); + mConnectivityManager.registerNetworkCallback( + new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED).build(), + tracker); + + if (enable) { + SystemUtil.runShellCommand("svc wifi enable"); + //noinspection deprecation + SystemUtil.runWithShellPermissionIdentity(mWifiManager::reconnect, + android.Manifest.permission.NETWORK_SETTINGS); + } else { + SystemUtil.runShellCommand("svc wifi disable"); + } + + tracker.waitForExpectedState(); + + assertEquals("Wifi must be " + (enable ? "connected to" : "disconnected from") + + " an access point for this test.", enable, isWiFiConnected()); + + mConnectivityManager.unregisterNetworkCallback(tracker); + } + + private boolean isWiFiConnected() { + return mWifiManager.isWifiEnabled() && mConnectivityManager.getActiveNetwork() != null + && !mConnectivityManager.isActiveNetworkMetered(); + } + + private class NetworkTracker extends ConnectivityManager.NetworkCallback { + private static final int MSG_CHECK_ACTIVE_NETWORK = 1; + + private final CountDownLatch mReceiveLatch = new CountDownLatch(1); + + private final boolean mExpectedMetered; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_CHECK_ACTIVE_NETWORK) { + checkActiveNetwork(); + } + } + }; + + private NetworkTracker(boolean expectedMetered) { + mExpectedMetered = expectedMetered; + } + + @Override + public void onAvailable(Network network, NetworkCapabilities networkCapabilities, + LinkProperties linkProperties, boolean blocked) { + checkActiveNetwork(); + } + + @Override + public void onLost(Network network) { + checkActiveNetwork(); + } + + boolean waitForExpectedState() throws InterruptedException { + checkActiveNetwork(); + return mReceiveLatch.await(60, TimeUnit.SECONDS); + } + + private void checkActiveNetwork() { + if (mReceiveLatch.getCount() == 0) { + return; + } + + if (mConnectivityManager.getActiveNetwork() != null + && mConnectivityManager.isActiveNetworkMetered() == mExpectedMetered) { + mReceiveLatch.countDown(); + } else { + mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000); + } + } + } +} diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java new file mode 100644 index 000000000000..dbe5773a3107 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsloadtests; + +import android.os.Process; + +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; + +import java.util.ArrayList; +import java.util.List; + +public class PowerMetrics { + private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; + private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; + private static final String PACKAGE_SYSTEMUI = "com.android.systemui"; + private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, + PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; + + enum MetricKind { + POWER, + DURATION, + } + + public static final String METRIC_APP_POWER = "appPower"; + public static final String METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL = "appPowerExcludeSystem"; + public static final String METRIC_APP_POWER_EXCLUDE_SMEARED = "appPowerExcludeSmeared"; + public static final String METRIC_SCREEN_POWER = "screenPower"; + public static final String METRIC_WIFI_POWER = "wifiPower"; + public static final String METRIC_SYSTEM_SERVICE_CPU_POWER = "systemService"; + public static final String METRIC_OTHER_POWER = "otherPower"; + public static final String METRIC_CPU_POWER = "cpuPower"; + public static final String METRIC_RAM_POWER = "ramPower"; + public static final String METRIC_WAKELOCK_POWER = "wakelockPower"; + public static final String METRIC_MOBILE_RADIO_POWER = "mobileRadioPower"; + public static final String METRIC_BLUETOOTH_POWER = "bluetoothPower"; + public static final String METRIC_GPS_POWER = "gpsPower"; + public static final String METRIC_CAMERA_POWER = "cameraPower"; + public static final String METRIC_FLASHLIGHT_POWER = "flashlightPower"; + public static final String METRIC_SENSORS_POWER = "sensorsPower"; + public static final String METRIC_AUDIO_POWER = "audioPower"; + public static final String METRIC_VIDEO_POWER = "videoPower"; + public static final String METRIC_CPU_TIME = "cpuTime"; + public static final String METRIC_CPU_FOREGROUND_TIME = "cpuForegroundTime"; + public static final String METRIC_WAKELOCK_TIME = "wakelockTime"; + public static final String METRIC_WIFI_RUNNING_TIME = "wifiRunningTime"; + public static final String METRIC_BLUETOOTH_RUNNING_TIME = "bluetoothRunningTime"; + public static final String METRIC_GPS_TIME = "gpsTime"; + public static final String METRIC_CAMERA_TIME = "cameraTime"; + public static final String METRIC_FLASHLIGHT_TIME = "flashlightTime"; + public static final String METRIC_AUDIO_TIME = "audioTime"; + public static final String METRIC_VIDEO_TIME = "videoTime"; + + public static class Metric { + public String metricType; + public MetricKind metricKind; + public String title; + public double value; + public double total; + } + + private final double mMinDrainedPower; + private final double mMaxDrainedPower; + + private List mMetrics = new ArrayList<>(); + + public PowerMetrics(BatteryStatsHelper batteryStatsHelper, int uid) { + mMinDrainedPower = batteryStatsHelper.getMinDrainedPower(); + mMaxDrainedPower = batteryStatsHelper.getMaxDrainedPower(); + + List usageList = batteryStatsHelper.getUsageList(); + + double totalPowerMah = 0; + double totalSmearedPowerMah = 0; + double totalPowerExcludeSystemMah = 0; + double totalScreenPower = 0; + double totalProportionalSmearMah = 0; + double totalCpuPowerMah = 0; + double totalSystemServiceCpuPowerMah = 0; + double totalUsagePowerMah = 0; + double totalWakeLockPowerMah = 0; + double totalMobileRadioPowerMah = 0; + double totalWifiPowerMah = 0; + double totalBluetoothPowerMah = 0; + double totalGpsPowerMah = 0; + double totalCameraPowerMah = 0; + double totalFlashlightPowerMah = 0; + double totalSensorPowerMah = 0; + double totalAudioPowerMah = 0; + double totalVideoPowerMah = 0; + + long totalCpuTimeMs = 0; + long totalCpuFgTimeMs = 0; + long totalWakeLockTimeMs = 0; + long totalWifiRunningTimeMs = 0; + long totalBluetoothRunningTimeMs = 0; + long totalGpsTimeMs = 0; + long totalCameraTimeMs = 0; + long totalFlashlightTimeMs = 0; + long totalAudioTimeMs = 0; + long totalVideoTimeMs = 0; + + BatterySipper uidSipper = null; + for (BatterySipper sipper : usageList) { + if (sipper.drainType == BatterySipper.DrainType.SCREEN) { + totalScreenPower = sipper.sumPower(); + } + + if (isHiddenDrainType(sipper.drainType)) { + continue; + } + + if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) { + uidSipper = sipper; + } + + totalPowerMah += sipper.sumPower(); + totalSmearedPowerMah += sipper.totalSmearedPowerMah; + totalProportionalSmearMah += sipper.proportionalSmearMah; + + if (!isSystemSipper(sipper)) { + totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah; + } + + totalCpuPowerMah += sipper.cpuPowerMah; + totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah; + totalUsagePowerMah += sipper.usagePowerMah; + totalWakeLockPowerMah += sipper.wakeLockPowerMah; + totalMobileRadioPowerMah += sipper.mobileRadioPowerMah; + totalWifiPowerMah += sipper.wifiPowerMah; + totalBluetoothPowerMah += sipper.bluetoothPowerMah; + totalGpsPowerMah += sipper.gpsPowerMah; + totalCameraPowerMah += sipper.cameraPowerMah; + totalFlashlightPowerMah += sipper.flashlightPowerMah; + totalSensorPowerMah += sipper.sensorPowerMah; + totalAudioPowerMah += sipper.audioPowerMah; + totalVideoPowerMah += sipper.videoPowerMah; + + totalCpuTimeMs += sipper.cpuTimeMs; + totalCpuFgTimeMs += sipper.cpuFgTimeMs; + totalWakeLockTimeMs += sipper.wakeLockTimeMs; + totalWifiRunningTimeMs += sipper.wifiRunningTimeMs; + totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs; + totalGpsTimeMs += sipper.gpsTimeMs; + totalCameraTimeMs += sipper.cameraTimeMs; + totalFlashlightTimeMs += sipper.flashlightTimeMs; + totalAudioTimeMs += sipper.audioTimeMs; + totalVideoTimeMs += sipper.videoTimeMs; + } + + if (uidSipper == null) { + return; + } + + addMetric(METRIC_APP_POWER, MetricKind.POWER, "Total power", + uidSipper.totalSmearedPowerMah, totalSmearedPowerMah); + addMetric(METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL, MetricKind.POWER, + "Total power excluding system", + uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah); + addMetric(METRIC_SCREEN_POWER, MetricKind.POWER, "Screen, smeared", + uidSipper.screenPowerMah, totalScreenPower); + addMetric(METRIC_OTHER_POWER, MetricKind.POWER, "Other, smeared", + uidSipper.proportionalSmearMah, totalProportionalSmearMah); + addMetric(METRIC_APP_POWER_EXCLUDE_SMEARED, MetricKind.POWER, "Excluding smeared", + uidSipper.totalPowerMah, totalPowerMah); + addMetric(METRIC_CPU_POWER, MetricKind.POWER, "CPU", + uidSipper.cpuPowerMah, totalCpuPowerMah); + addMetric(METRIC_SYSTEM_SERVICE_CPU_POWER, MetricKind.POWER, "System services", + uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah); + addMetric(METRIC_RAM_POWER, MetricKind.POWER, "RAM", + uidSipper.usagePowerMah, totalUsagePowerMah); + addMetric(METRIC_WAKELOCK_POWER, MetricKind.POWER, "Wake lock", + uidSipper.wakeLockPowerMah, totalWakeLockPowerMah); + addMetric(METRIC_MOBILE_RADIO_POWER, MetricKind.POWER, "Mobile radio", + uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah); + addMetric(METRIC_WIFI_POWER, MetricKind.POWER, "WiFi", + uidSipper.wifiPowerMah, totalWifiPowerMah); + addMetric(METRIC_BLUETOOTH_POWER, MetricKind.POWER, "Bluetooth", + uidSipper.bluetoothPowerMah, totalBluetoothPowerMah); + addMetric(METRIC_GPS_POWER, MetricKind.POWER, "GPS", + uidSipper.gpsPowerMah, totalGpsPowerMah); + addMetric(METRIC_CAMERA_POWER, MetricKind.POWER, "Camera", + uidSipper.cameraPowerMah, totalCameraPowerMah); + addMetric(METRIC_FLASHLIGHT_POWER, MetricKind.POWER, "Flashlight", + uidSipper.flashlightPowerMah, totalFlashlightPowerMah); + addMetric(METRIC_SENSORS_POWER, MetricKind.POWER, "Sensors", + uidSipper.sensorPowerMah, totalSensorPowerMah); + addMetric(METRIC_AUDIO_POWER, MetricKind.POWER, "Audio", + uidSipper.audioPowerMah, totalAudioPowerMah); + addMetric(METRIC_VIDEO_POWER, MetricKind.POWER, "Video", + uidSipper.videoPowerMah, totalVideoPowerMah); + + addMetric(METRIC_CPU_TIME, MetricKind.DURATION, "CPU time", + uidSipper.cpuTimeMs, totalCpuTimeMs); + addMetric(METRIC_CPU_FOREGROUND_TIME, MetricKind.DURATION, "CPU foreground time", + uidSipper.cpuFgTimeMs, totalCpuFgTimeMs); + addMetric(METRIC_WAKELOCK_TIME, MetricKind.DURATION, "Wake lock time", + uidSipper.wakeLockTimeMs, totalWakeLockTimeMs); + addMetric(METRIC_WIFI_RUNNING_TIME, MetricKind.DURATION, "WiFi running time", + uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs); + addMetric(METRIC_BLUETOOTH_RUNNING_TIME, MetricKind.DURATION, "Bluetooth time", + uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs); + addMetric(METRIC_GPS_TIME, MetricKind.DURATION, "GPS time", + uidSipper.gpsTimeMs, totalGpsTimeMs); + addMetric(METRIC_CAMERA_TIME, MetricKind.DURATION, "Camera time", + uidSipper.cameraTimeMs, totalCameraTimeMs); + addMetric(METRIC_FLASHLIGHT_TIME, MetricKind.DURATION, "Flashlight time", + uidSipper.flashlightTimeMs, totalFlashlightTimeMs); + addMetric(METRIC_AUDIO_TIME, MetricKind.DURATION, "Audio time", + uidSipper.audioTimeMs, totalAudioTimeMs); + addMetric(METRIC_VIDEO_TIME, MetricKind.DURATION, "Video time", + uidSipper.videoTimeMs, totalVideoTimeMs); + } + + public List getMetrics() { + return mMetrics; + } + + public double getMinDrainedPower() { + return mMinDrainedPower; + } + + public double getMaxDrainedPower() { + return mMaxDrainedPower; + } + + protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) { + return drainType == BatterySipper.DrainType.IDLE + || drainType == BatterySipper.DrainType.CELL + || drainType == BatterySipper.DrainType.SCREEN + || drainType == BatterySipper.DrainType.UNACCOUNTED + || drainType == BatterySipper.DrainType.OVERCOUNTED + || drainType == BatterySipper.DrainType.BLUETOOTH + || drainType == BatterySipper.DrainType.WIFI; + } + + private boolean isSystemSipper(BatterySipper sipper) { + final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); + if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { + return true; + } else if (sipper.mPackages != null) { + for (final String packageName : sipper.mPackages) { + for (final String systemPackage : PACKAGES_SYSTEM) { + if (systemPackage.equals(packageName)) { + return true; + } + } + } + } + + return false; + } + + private void addMetric(String metricType, MetricKind metricKind, String title, double amount, + double totalAmount) { + Metric metric = new Metric(); + metric.metricType = metricType; + metric.metricKind = metricKind; + metric.title = title; + metric.value = amount; + metric.total = totalAmount; + mMetrics.add(metric); + } +} diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java new file mode 100644 index 000000000000..254458cc8935 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsloadtests; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.app.Instrumentation; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.Bundle; +import android.os.Process; +import android.os.SystemClock; +import android.os.UserManager; +import android.util.Log; +import android.util.TimeUtils; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.SystemUtil; +import com.android.internal.os.BatteryStatsHelper; +import com.android.internal.os.LoggingPrintStream; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class PowerMetricsCollector implements TestRule { + private final String mTag; + private final float mBatteryDrainThresholdPct; + private final int mTimeoutMillis; + + private final Context mContext; + private final UserManager mUserManager; + private final int mUid; + private final BatteryStatsHelper mStatsHelper; + private final CountDownLatch mSuspendingBatteryInput = new CountDownLatch(1); + + private long mStartTime; + private volatile float mInitialBatteryLevel; + private volatile float mCurrentBatteryLevel; + private int mIterations; + private PowerMetrics mInitialPowerMetrics; + private PowerMetrics mFinalPowerMetrics; + private List mPowerMetricsDelta; + private Intent mBatteryStatus; + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + BroadcastReceiver batteryBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + handleBatteryStatus(intent); + } + }; + mBatteryStatus = mContext.registerReceiver(batteryBroadcastReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + disableCharger(); + try { + prepareBatteryLevelMonitor(); + mStartTime = SystemClock.uptimeMillis(); + base.evaluate(); + captureFinalPowerStatsData(); + } finally { + mContext.unregisterReceiver(batteryBroadcastReceiver); + enableCharger(); + } + } + }; + } + + public PowerMetricsCollector(String tag, float batteryDrainThresholdPct, int timeoutMillis) { + mTag = tag; + mBatteryDrainThresholdPct = batteryDrainThresholdPct; + mTimeoutMillis = timeoutMillis; + + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + mContext = instrumentation.getContext(); + mUid = Process.myUid(); + mUserManager = mContext.getSystemService(UserManager.class); + // TODO(b/175324611): Use BatteryUsageStats instead + mStatsHelper = new BatteryStatsHelper(mContext, false /* collectBatteryBroadcast */); + mStatsHelper.create((Bundle) null); + } + + private void disableCharger() throws InterruptedException { + SystemUtil.runShellCommand("dumpsys battery suspend_input"); + final boolean success = mSuspendingBatteryInput.await(10, TimeUnit.SECONDS); + assertTrue("Timed out waiting for battery input to be suspended", success); + } + + private void enableCharger() { + SystemUtil.runShellCommand("dumpsys battery reset"); + } + + private PowerMetrics readBatteryStatsData() { + mStatsHelper.clearStats(); + mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, + mUserManager.getUserProfiles()); + return new PowerMetrics(mStatsHelper, mUid); + } + + protected void prepareBatteryLevelMonitor() { + handleBatteryStatus(mBatteryStatus); + mInitialBatteryLevel = mCurrentBatteryLevel; + } + + protected void handleBatteryStatus(Intent intent) { + if (mFinalPowerMetrics != null) { + return; + } + + final boolean isCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0; + + if (mSuspendingBatteryInput.getCount() > 0) { + if (!isCharging) { + mSuspendingBatteryInput.countDown(); + } + return; + } + + if (isCharging) { + fail("Device must remain disconnected from the power source " + + "for the duration of the test"); + } + + int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); + + mCurrentBatteryLevel = level * 100 / (float) scale; + Log.i(mTag, "Battery level = " + mCurrentBatteryLevel); + + // We delay tracking until the battery level drops. If the resolution of + // battery level is 1%, and the initially reported level is 73, we don't know whether + // it's 73.1 or 73.7. Once it drops to 72, we can be confident that the real battery + // level it is very close to 72.0 and can start tracking. + if (mInitialPowerMetrics == null && mCurrentBatteryLevel < mInitialBatteryLevel) { + mInitialBatteryLevel = mCurrentBatteryLevel; + mInitialPowerMetrics = readBatteryStatsData(); + } + } + + private void captureFinalPowerStatsData() { + if (mFinalPowerMetrics != null) { + return; + } + + mFinalPowerMetrics = readBatteryStatsData(); + + mPowerMetricsDelta = new ArrayList<>(); + List initialPowerMetrics = mInitialPowerMetrics.getMetrics(); + List finalPowerMetrics = mFinalPowerMetrics.getMetrics(); + for (PowerMetrics.Metric initialMetric : initialPowerMetrics) { + PowerMetrics.Metric finalMetric = null; + for (PowerMetrics.Metric metric : finalPowerMetrics) { + if (metric.title.equals(initialMetric.title)) { + finalMetric = metric; + break; + } + } + + if (finalMetric != null) { + PowerMetrics.Metric delta = new PowerMetrics.Metric(); + delta.metricType = initialMetric.metricType; + delta.metricKind = initialMetric.metricKind; + delta.title = initialMetric.title; + delta.total = finalMetric.total - initialMetric.total; + delta.value = finalMetric.value - initialMetric.value; + mPowerMetricsDelta.add(delta); + } + } + } + + /** + * Returns false if sufficient data has been accumulated. + */ + public boolean checkpoint() { + long elapsedTime = SystemClock.uptimeMillis() - mStartTime; + if (elapsedTime >= mTimeoutMillis) { + Log.i(mTag, "Timeout reached " + TimeUtils.formatDuration(elapsedTime)); + captureFinalPowerStatsData(); + return false; + } + + if (mInitialPowerMetrics == null) { + return true; + } + + if (mInitialBatteryLevel - mCurrentBatteryLevel >= mBatteryDrainThresholdPct) { + Log.i(mTag, + "Battery drain reached " + (mInitialBatteryLevel - mCurrentBatteryLevel) + "%"); + captureFinalPowerStatsData(); + return false; + } + + mIterations++; + return true; + } + + + public int getIterationCount() { + return mIterations; + } + + public void dumpMetrics() { + dumpMetrics(new LoggingPrintStream() { + @Override + protected void log(String line) { + Log.i(mTag, line); + } + }); + } + + public void dumpMetrics(PrintStream out) { + List initialPowerMetrics = mInitialPowerMetrics.getMetrics(); + List finalPowerMetrics = mFinalPowerMetrics.getMetrics(); + + out.println("== Power metrics at test start"); + dumpPowerStatsData(out, initialPowerMetrics); + + out.println("== Power metrics at test end"); + dumpPowerStatsData(out, finalPowerMetrics); + + out.println("== Power metrics delta"); + dumpPowerStatsData(out, mPowerMetricsDelta); + } + + protected void dumpPowerStatsData(PrintStream out, List metrics) { + Locale locale = Locale.getDefault(); + for (PowerMetrics.Metric metric : metrics) { + double proportion = metric.total != 0 ? metric.value * 100 / metric.total : 0; + switch (metric.metricKind) { + case POWER: + out.println( + String.format(locale, " %-30s %7.1f mAh %4.1f%%", metric.title, + metric.value, proportion)); + break; + case DURATION: + out.println( + String.format(locale, " %-30s %,7d ms %4.1f%%", metric.title, + (long) metric.value, proportion)); + break; + } + } + } + + public void dumpMetricAsPercentageOfDrainedPower(String metricType) { + double minDrainedPower = + mFinalPowerMetrics.getMinDrainedPower() - mInitialPowerMetrics.getMinDrainedPower(); + double maxDrainedPower = + mFinalPowerMetrics.getMaxDrainedPower() - mInitialPowerMetrics.getMaxDrainedPower(); + + PowerMetrics.Metric metric = getMetric(metricType); + double metricDelta = metric.value; + + if (maxDrainedPower - minDrainedPower < 0.1f) { + Log.i(mTag, String.format(Locale.getDefault(), + "%s power consumed by the test: %.1f of %.1f mAh (%.1f%%)", + metric.title, metricDelta, maxDrainedPower, + metricDelta / maxDrainedPower * 100)); + } else { + Log.i(mTag, String.format(Locale.getDefault(), + "%s power consumed by the test: %.1f of %.1f - %.1f mAh (%.1f%% - %.1f%%)", + metric.title, metricDelta, minDrainedPower, maxDrainedPower, + metricDelta / minDrainedPower * 100, metricDelta / maxDrainedPower * 100)); + } + } + + public PowerMetrics.Metric getMetric(String metricType) { + for (PowerMetrics.Metric metric : mPowerMetricsDelta) { + if (metric.metricType.equals(metricType)) { + return metric; + } + } + return null; + } +} diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/SystemServiceCallLoadTest.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/SystemServiceCallLoadTest.java new file mode 100644 index 000000000000..488469d54f29 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/SystemServiceCallLoadTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsloadtests; + +import static org.junit.Assert.assertNotNull; + +import android.app.Instrumentation; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class SystemServiceCallLoadTest { + private static final String TAG = "SystemServiceCallLoadTest"; + private static final int TIMEOUT_MILLIS = 60 * 60 * 1000; + private static final float BATTERY_DRAIN_THRESHOLD_PCT = 2.99f; + + @Rule + public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG, + BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS); + + private PackageManager mPackageManager; + + @Before + public void setup() { + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + mPackageManager = instrumentation.getContext().getPackageManager(); + } + + @Test + public void test() { + while (mPowerMetricsCollector.checkpoint()) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.parse("http://example.com/"), "text/plain"); + intent.addCategory(Intent.CATEGORY_BROWSABLE); + ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); + assertNotNull(resolveInfo); + } + + mPowerMetricsCollector.dumpMetrics(); + + Log.i(TAG, "=="); + Log.i(TAG, "Total system server calls made " + mPowerMetricsCollector.getIterationCount()); + + mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower( + PowerMetrics.METRIC_SYSTEM_SERVICE_CPU_POWER); + } +} diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/WiFiLoadTest.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/WiFiLoadTest.java new file mode 100644 index 000000000000..27495da8d839 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/WiFiLoadTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsloadtests; + +import android.util.Log; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +public class WiFiLoadTest { + private static final String TAG = "WiFiLoadTest"; + private static final String DOWNLOAD_TEST_URL = + "https://i.ytimg.com/vi/l5mE3Tpjejs/maxresdefault.jpg"; + + private static final int TIMEOUT_MILLIS = 60 * 60 * 1000; + private static final float BATTERY_DRAIN_THRESHOLD_PCT = 0.99f; + + @Rule + public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG, + BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS); + + @Rule + public ConnectivitySetupRule mConnectivitySetupRule = + new ConnectivitySetupRule(/* WiFi enabled */true); + + @Test + public void test() throws IOException { + long totalBytesRead = 0; + URL url = new URL(DOWNLOAD_TEST_URL); + byte[] buffer = new byte[131072]; // Large buffer to minimize CPU usage + + while (mPowerMetricsCollector.checkpoint()) { + try (InputStream inputStream = url.openStream()) { + while (true) { + int count = inputStream.read(buffer); + if (count < 0) { + break; + } + totalBytesRead += count; + } + } + } + + mPowerMetricsCollector.dumpMetrics(); + + Log.i(TAG, "=="); + Log.i(TAG, "WiFi running time: " + (long) mPowerMetricsCollector.getMetric( + PowerMetrics.METRIC_WIFI_RUNNING_TIME).value); + Log.i(TAG, "Total bytes read over WiFi: " + totalBytesRead); + + mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower( + PowerMetrics.METRIC_WIFI_POWER); + } +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp new file mode 100644 index 000000000000..1e0498be5800 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp @@ -0,0 +1,13 @@ +android_test { + name: "BatteryStatsViewer", + srcs: ["src/**/*.java"], + defaults: ["SettingsLibDefaults"], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.cardview_cardview", + "androidx.recyclerview_recyclerview", + "com.google.android.material_material", + ], + platform_apis: true, + certificate: "platform", +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml new file mode 100644 index 000000000000..edb1b108b7bc --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml new file mode 100644 index 000000000000..1ced825adf31 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_info_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_info_layout.xml new file mode 100644 index 000000000000..fbd0ebd61bc7 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_info_layout.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_activity_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_activity_layout.xml new file mode 100644 index 000000000000..ecc89f0cb33e --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_activity_layout.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml new file mode 100644 index 000000000000..bea38c18c20b --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml @@ -0,0 +1,35 @@ + + + + + + + + \ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml new file mode 100644 index 000000000000..e58a08fd362c --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml new file mode 100644 index 000000000000..629d729e7b9a --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java new file mode 100644 index 000000000000..b077ea313b9d --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsviewer; + +import android.content.Context; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.Process; +import android.os.UidBatteryConsumer; +import android.os.UserHandle; + +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; + +import java.util.ArrayList; +import java.util.List; + +public class BatteryConsumerData { + private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; + private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; + private static final String PACKAGE_SYSTEMUI = "com.android.systemui"; + private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, + PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; + + // Temporary placeholder voltage for converting energy to charge + // TODO: remove this when b/173765509 is resolved + private static final double MOCK_NOMINAL_VOLTAGE = 3.7; + + // Unit conversion: + // mAh = uWs * (1/1000)(milli/micro) * (1/Voltage) * (1/3600)(hours/second) + private static final double UJ_2_MAH = + (1.0 / 1000) * (1.0 / MOCK_NOMINAL_VOLTAGE) * (1.0 / 3600); + + enum EntryType { + POWER, + DURATION, + } + + public static class Entry { + public String title; + public EntryType entryType; + public double value; + public double total; + } + + private final BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo; + private final List mEntries = new ArrayList<>(); + + public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper, + BatteryUsageStats batteryUsageStats, String batteryConsumerId) { + List usageList = batteryStatsHelper.getUsageList(); + BatteryStats batteryStats = batteryStatsHelper.getStats(); + + double totalPowerMah = 0; + double totalSmearedPowerMah = 0; + double totalPowerExcludeSystemMah = 0; + double totalScreenPower = 0; + double totalProportionalSmearMah = 0; + double totalCpuPowerMah = 0; + double totalSystemServiceCpuPowerMah = 0; + double totalUsagePowerMah = 0; + double totalWakeLockPowerMah = 0; + double totalMobileRadioPowerMah = 0; + double totalWifiPowerMah = 0; + double totalBluetoothPowerMah = 0; + double totalGpsPowerMah = 0; + double totalCameraPowerMah = 0; + double totalFlashlightPowerMah = 0; + double totalSensorPowerMah = 0; + double totalAudioPowerMah = 0; + double totalVideoPowerMah = 0; + + long totalCpuTimeMs = 0; + long totalCpuFgTimeMs = 0; + long totalWakeLockTimeMs = 0; + long totalWifiRunningTimeMs = 0; + long totalBluetoothRunningTimeMs = 0; + long totalGpsTimeMs = 0; + long totalCameraTimeMs = 0; + long totalFlashlightTimeMs = 0; + long totalAudioTimeMs = 0; + long totalVideoTimeMs = 0; + + BatterySipper requestedBatterySipper = null; + for (BatterySipper sipper : usageList) { + if (sipper.drainType == BatterySipper.DrainType.SCREEN) { + totalScreenPower = sipper.sumPower(); + } + + if (batteryConsumerId(sipper).equals(batteryConsumerId)) { + requestedBatterySipper = sipper; + } + + totalPowerMah += sipper.sumPower(); + totalSmearedPowerMah += sipper.totalSmearedPowerMah; + totalProportionalSmearMah += sipper.proportionalSmearMah; + + if (!isSystemSipper(sipper)) { + totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah; + } + + totalCpuPowerMah += sipper.cpuPowerMah; + totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah; + totalUsagePowerMah += sipper.usagePowerMah; + totalWakeLockPowerMah += sipper.wakeLockPowerMah; + totalMobileRadioPowerMah += sipper.mobileRadioPowerMah; + totalWifiPowerMah += sipper.wifiPowerMah; + totalBluetoothPowerMah += sipper.bluetoothPowerMah; + totalGpsPowerMah += sipper.gpsPowerMah; + totalCameraPowerMah += sipper.cameraPowerMah; + totalFlashlightPowerMah += sipper.flashlightPowerMah; + totalSensorPowerMah += sipper.sensorPowerMah; + totalAudioPowerMah += sipper.audioPowerMah; + totalVideoPowerMah += sipper.videoPowerMah; + + totalCpuTimeMs += sipper.cpuTimeMs; + totalCpuFgTimeMs += sipper.cpuFgTimeMs; + totalWakeLockTimeMs += sipper.wakeLockTimeMs; + totalWifiRunningTimeMs += sipper.wifiRunningTimeMs; + totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs; + totalGpsTimeMs += sipper.gpsTimeMs; + totalCameraTimeMs += sipper.cameraTimeMs; + totalFlashlightTimeMs += sipper.flashlightTimeMs; + totalAudioTimeMs += sipper.audioTimeMs; + totalVideoTimeMs += sipper.videoTimeMs; + } + + BatteryConsumer requestedBatteryConsumer = null; + + for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { + if (batteryConsumerId(consumer).equals(batteryConsumerId)) { + requestedBatteryConsumer = consumer; + break; + } + } + + if (requestedBatterySipper == null) { + mBatteryConsumerInfo = null; + return; + } + + mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo( + context.getPackageManager(), requestedBatterySipper); + long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + + addEntry("Total power", EntryType.POWER, + requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah); + maybeAddMeasuredEnergyEntry(requestedBatterySipper.drainType, batteryStats); + + addEntry("... excluding system", EntryType.POWER, + requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah); + addEntry("Screen, smeared", EntryType.POWER, + requestedBatterySipper.screenPowerMah, totalScreenPower); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + final double ratio = measuredCharge / totalScreenPower; + addEntry("Screen, smeared (PowerStatsHal adjusted)", EntryType.POWER, + requestedBatterySipper.screenPowerMah * ratio, measuredCharge); + } + addEntry("Other, smeared", EntryType.POWER, + requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah); + addEntry("Excluding smeared", EntryType.POWER, + requestedBatterySipper.totalPowerMah, totalPowerMah); + if (requestedBatteryConsumer != null) { + addEntry("CPU", EntryType.POWER, + requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU), + totalCpuPowerMah); + } + addEntry("CPU (sipper)", EntryType.POWER, + requestedBatterySipper.cpuPowerMah, totalCpuPowerMah); + addEntry("System services", EntryType.POWER, + requestedBatterySipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah); + addEntry("Usage", EntryType.POWER, + requestedBatterySipper.usagePowerMah, totalUsagePowerMah); + addEntry("Wake lock", EntryType.POWER, + requestedBatterySipper.wakeLockPowerMah, totalWakeLockPowerMah); + addEntry("Mobile radio", EntryType.POWER, + requestedBatterySipper.mobileRadioPowerMah, totalMobileRadioPowerMah); + addEntry("WiFi", EntryType.POWER, + requestedBatterySipper.wifiPowerMah, totalWifiPowerMah); + addEntry("Bluetooth", EntryType.POWER, + requestedBatterySipper.bluetoothPowerMah, totalBluetoothPowerMah); + addEntry("GPS", EntryType.POWER, + requestedBatterySipper.gpsPowerMah, totalGpsPowerMah); + addEntry("Camera", EntryType.POWER, + requestedBatterySipper.cameraPowerMah, totalCameraPowerMah); + addEntry("Flashlight", EntryType.POWER, + requestedBatterySipper.flashlightPowerMah, totalFlashlightPowerMah); + addEntry("Sensors", EntryType.POWER, + requestedBatterySipper.sensorPowerMah, totalSensorPowerMah); + addEntry("Audio", EntryType.POWER, + requestedBatterySipper.audioPowerMah, totalAudioPowerMah); + addEntry("Video", EntryType.POWER, + requestedBatterySipper.videoPowerMah, totalVideoPowerMah); + + addEntry("CPU time", EntryType.DURATION, + requestedBatterySipper.cpuTimeMs, totalCpuTimeMs); + addEntry("CPU foreground time", EntryType.DURATION, + requestedBatterySipper.cpuFgTimeMs, totalCpuFgTimeMs); + addEntry("Wake lock time", EntryType.DURATION, + requestedBatterySipper.wakeLockTimeMs, totalWakeLockTimeMs); + addEntry("WiFi running time", EntryType.DURATION, + requestedBatterySipper.wifiRunningTimeMs, totalWifiRunningTimeMs); + addEntry("Bluetooth time", EntryType.DURATION, + requestedBatterySipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs); + addEntry("GPS time", EntryType.DURATION, + requestedBatterySipper.gpsTimeMs, totalGpsTimeMs); + addEntry("Camera time", EntryType.DURATION, + requestedBatterySipper.cameraTimeMs, totalCameraTimeMs); + addEntry("Flashlight time", EntryType.DURATION, + requestedBatterySipper.flashlightTimeMs, totalFlashlightTimeMs); + addEntry("Audio time", EntryType.DURATION, + requestedBatterySipper.audioTimeMs, totalAudioTimeMs); + addEntry("Video time", EntryType.DURATION, + requestedBatterySipper.videoTimeMs, totalVideoTimeMs); + } + + private boolean isSystemSipper(BatterySipper sipper) { + final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); + if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { + return true; + } else if (sipper.mPackages != null) { + for (final String packageName : sipper.mPackages) { + for (final String systemPackage : PACKAGES_SYSTEM) { + if (systemPackage.equals(packageName)) { + return true; + } + } + } + } + + return false; + } + + private void addEntry(String title, EntryType entryType, double amount, double totalAmount) { + Entry entry = new Entry(); + entry.title = title; + entry.entryType = entryType; + entry.value = amount; + entry.total = totalAmount; + mEntries.add(entry); + } + + private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType, + BatteryStats batteryStats) { + switch (drainType) { + case AMBIENT_DISPLAY: + final long totalDozeMeasuredEnergyUJ = batteryStats.getScreenDozeEnergy(); + if (totalDozeMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalDozeMeasuredEnergyUJ; + addEntry("Measured ambient display power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + case SCREEN: + final long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + addEntry("Measured screen power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + } + } + + public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() { + return mBatteryConsumerInfo; + } + + public List getEntries() { + return mEntries; + } + + public static String batteryConsumerId(BatterySipper sipper) { + return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid(); + } + + public static String batteryConsumerId(BatteryConsumer consumer) { + if (consumer instanceof UidBatteryConsumer) { + return BatterySipper.DrainType.APP + "|" + + UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|" + + ((UidBatteryConsumer) consumer).getUid(); + } else { + return ""; + } + } +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java new file mode 100644 index 000000000000..8ee6c604cb3e --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsviewer; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Process; + +import androidx.annotation.NonNull; + +import com.android.internal.os.BatterySipper; + +import java.util.Locale; + +class BatteryConsumerInfoHelper { + + private static final String SYSTEM_SERVER_PACKAGE_NAME = "android"; + + public static class BatteryConsumerInfo { + public String id; + public CharSequence label; + public double powerMah; + public ApplicationInfo iconInfo; + public CharSequence packages; + public CharSequence details; + } + + @NonNull + public static BatteryConsumerInfo makeBatteryConsumerInfo(PackageManager packageManager, + @NonNull BatterySipper sipper) { + BatteryConsumerInfo info = new BatteryConsumerInfo(); + info.id = BatteryConsumerData.batteryConsumerId(sipper); + sipper.sumPower(); + info.powerMah = sipper.totalSmearedPowerMah; + switch (sipper.drainType) { + case APP: { + int uid = sipper.getUid(); + info.details = String.format("UID: %d", uid); + String packageWithHighestDrain = sipper.packageWithHighestDrain; + if (uid == Process.ROOT_UID) { + info.label = ""; + } else { + String[] packages = packageManager.getPackagesForUid(uid); + String primaryPackageName = null; + if (uid == Process.SYSTEM_UID) { + primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME; + } else if (packages != null) { + for (String name : packages) { + primaryPackageName = name; + if (name.equals(packageWithHighestDrain)) { + break; + } + } + } + + if (primaryPackageName != null) { + try { + ApplicationInfo applicationInfo = + packageManager.getApplicationInfo(primaryPackageName, 0); + info.label = applicationInfo.loadLabel(packageManager); + info.iconInfo = applicationInfo; + } catch (PackageManager.NameNotFoundException e) { + info.label = primaryPackageName; + } + } else if (packageWithHighestDrain != null) { + info.label = packageWithHighestDrain; + } + + if (packages != null && packages.length > 0) { + StringBuilder sb = new StringBuilder(); + if (primaryPackageName != null) { + sb.append(primaryPackageName); + } + for (String packageName : packages) { + if (packageName.equals(primaryPackageName)) { + continue; + } + + if (sb.length() != 0) { + sb.append(", "); + } + sb.append(packageName); + } + + info.packages = sb; + } + } + break; + } + case USER: + info.label = "User"; + info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId); + break; + case AMBIENT_DISPLAY: + info.label = "Ambient display"; + break; + case BLUETOOTH: + info.label = "Bluetooth"; + break; + case CAMERA: + info.label = "Camera"; + break; + case CELL: + info.label = "Cell"; + break; + case FLASHLIGHT: + info.label = "Flashlight"; + break; + case IDLE: + info.label = "Idle"; + break; + case MEMORY: + info.label = "Memory"; + break; + case OVERCOUNTED: + info.label = "Overcounted"; + break; + case PHONE: + info.label = "Phone"; + break; + case SCREEN: + info.label = "Screen"; + break; + case UNACCOUNTED: + info.label = "Unaccounted"; + break; + case WIFI: + info.label = "WiFi"; + break; + } + // Default the app icon to System Server. This includes root, dex2oat and other UIDs. + if (info.iconInfo == null) { + try { + info.iconInfo = + packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException nameNotFoundException) { + // Won't happen + } + } + return info; + } +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java new file mode 100644 index 000000000000..2db848b084a6 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsviewer; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.activity.result.contract.ActivityResultContract; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.tabs.TabLayout; + +/** + * Picker, showing a sorted lists of applications and other types of entities consuming power. + * Returns the selected entity ID or null. + */ +public class BatteryConsumerPickerActivity extends FragmentActivity { + + public static final ActivityResultContract CONTRACT = + new ActivityResultContract() { + @NonNull + @Override + public Intent createIntent(@NonNull Context context, Void aVoid) { + return new Intent(context, BatteryConsumerPickerActivity.class); + } + + @Override + public String parseResult(int resultCode, @Nullable Intent intent) { + if (resultCode != RESULT_OK || intent == null) { + return null; + } + return intent.getStringExtra(Intent.EXTRA_RETURN_RESULT); + } + }; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + getActionBar().setDisplayHomeAsUpEnabled(true); + + setContentView(R.layout.battery_consumer_picker_activity_layout); + + ViewPager viewPager = findViewById(R.id.pager); + + FragmentStatePagerAdapter adapter = new FragmentStatePagerAdapter( + getSupportFragmentManager()) { + + @Override + public int getCount() { + return 2; + } + + @NonNull + @Override + public Fragment getItem(int position) { + switch (position) { + case 0: + return new BatteryConsumerPickerFragment( + BatteryConsumerPickerFragment.PICKER_TYPE_APP); + case 1: + default: + return new BatteryConsumerPickerFragment( + BatteryConsumerPickerFragment.PICKER_TYPE_DRAIN); + } + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case 0: + return "Apps"; + case 1: + return "Drains"; + } + return null; + } + }; + + viewPager.setAdapter(adapter); + TabLayout tabLayout = findViewById(R.id.tab_layout); + tabLayout.setupWithViewPager(viewPager); + } + + public void setSelectedBatteryConsumer(String batteryConsumerId) { + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_RETURN_RESULT, batteryConsumerId); + setResult(RESULT_OK, intent); + finish(); + } + + @Override + public boolean onNavigateUp() { + onBackPressed(); + return true; + } +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java new file mode 100644 index 000000000000..bb11fd598511 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2008 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.frameworks.core.batterystatsviewer; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.BatteryStats; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.frameworks.core.batterystatsviewer.BatteryConsumerInfoHelper.BatteryConsumerInfo; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +import com.android.settingslib.utils.AsyncLoaderCompat; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +/** + * Picker, showing a sorted lists of applications or other types of entities consuming power. + * Returns the selected entity ID or null. + */ +public class BatteryConsumerPickerFragment extends Fragment { + private static final String TAG = "AppPicker"; + + public static final String PICKER_TYPE = "pickertype"; + + public static final int PICKER_TYPE_APP = 0; + public static final int PICKER_TYPE_DRAIN = 1; + + private BatteryConsumerListAdapter mBatteryConsumerListAdapter; + private RecyclerView mAppList; + private View mLoadingView; + + private interface OnBatteryConsumerSelectedListener { + void onBatteryConsumerSelected(String batteryConsumerId); + } + + public BatteryConsumerPickerFragment(int pickerType) { + Bundle args = new Bundle(); + args.putInt(PICKER_TYPE, pickerType); + setArguments(args); + } + + public BatteryConsumerPickerFragment() { + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.battery_consumer_picker_layout, container, false); + mLoadingView = view.findViewById(R.id.loading_view); + + mAppList = view.findViewById(R.id.list_view); + mAppList.setLayoutManager(new LinearLayoutManager(getContext())); + mBatteryConsumerListAdapter = new BatteryConsumerListAdapter( + BatteryConsumerPickerFragment.this::setSelectedBatteryConsumer); + mAppList.setAdapter(mBatteryConsumerListAdapter); + + LoaderManager.getInstance(this).initLoader(0, getArguments(), + new BatteryConsumerListLoaderCallbacks()); + return view; + } + + public void setSelectedBatteryConsumer(String id) { + ((BatteryConsumerPickerActivity) getActivity()).setSelectedBatteryConsumer(id); + } + + private static class BatteryConsumerListLoader extends + AsyncLoaderCompat> { + private final BatteryStatsHelper mStatsHelper; + private final int mPickerType; + private final UserManager mUserManager; + private final PackageManager mPackageManager; + + BatteryConsumerListLoader(Context context, int pickerType) { + super(context); + mUserManager = context.getSystemService(UserManager.class); + mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */); + mPickerType = pickerType; + mStatsHelper.create((Bundle) null); + mStatsHelper.clearStats(); + mPackageManager = context.getPackageManager(); + } + + @Override + public List loadInBackground() { + List batteryConsumerList = new ArrayList<>(); + + mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); + + final List usageList = mStatsHelper.getUsageList(); + for (BatterySipper sipper : usageList) { + switch (mPickerType) { + case PICKER_TYPE_APP: + if (sipper.drainType != BatterySipper.DrainType.APP) { + continue; + } + break; + case PICKER_TYPE_DRAIN: + default: + if (sipper.drainType == BatterySipper.DrainType.APP) { + continue; + } + } + + batteryConsumerList.add( + BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, sipper)); + } + + batteryConsumerList.sort( + Comparator.comparing((BatteryConsumerInfo a) -> a.powerMah).reversed()); + return batteryConsumerList; + } + + @Override + protected void onDiscardResult(List result) { + } + } + + private class BatteryConsumerListLoaderCallbacks implements + LoaderManager.LoaderCallbacks> { + + @NonNull + @Override + public Loader> onCreateLoader(int id, Bundle args) { + return new BatteryConsumerListLoader(getContext(), args.getInt(PICKER_TYPE)); + } + + @Override + public void onLoadFinished(@NonNull Loader> loader, + List batteryConsumerList) { + mBatteryConsumerListAdapter.setBatteryConsumerList(batteryConsumerList); + mAppList.setVisibility(View.VISIBLE); + mLoadingView.setVisibility(View.GONE); + } + + @Override + public void onLoaderReset( + @NonNull Loader> loader) { + } + } + + public class BatteryConsumerListAdapter extends + RecyclerView.Adapter { + private final OnBatteryConsumerSelectedListener mListener; + private List mBatteryConsumerList; + + public BatteryConsumerListAdapter(OnBatteryConsumerSelectedListener listener) { + mListener = listener; + } + + void setBatteryConsumerList(List batteryConsumerList) { + mBatteryConsumerList = batteryConsumerList; + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return mBatteryConsumerList.size(); + } + + @NonNull + @Override + public BatteryConsumerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, + int position) { + LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext()); + View view = layoutInflater.inflate(R.layout.battery_consumer_info_layout, viewGroup, + false); + return new BatteryConsumerViewHolder(view, mListener); + } + + @Override + public void onBindViewHolder(@NonNull BatteryConsumerViewHolder viewHolder, int position) { + BatteryConsumerInfo item = mBatteryConsumerList.get(position); + viewHolder.id = item.id; + viewHolder.titleView.setText(item.label); + if (item.details != null) { + viewHolder.detailsView.setText(item.details); + viewHolder.detailsView.setVisibility(View.VISIBLE); + } else { + viewHolder.detailsView.setVisibility(View.GONE); + } + viewHolder.powerView.setText( + String.format(Locale.getDefault(), "%.1f mAh", item.powerMah)); + viewHolder.iconView.setImageDrawable( + item.iconInfo.loadIcon(getContext().getPackageManager())); + if (item.packages != null) { + viewHolder.packagesView.setText(item.packages); + viewHolder.packagesView.setVisibility(View.VISIBLE); + } else { + viewHolder.packagesView.setVisibility(View.GONE); + } + } + } + + // View Holder used when displaying apps + public static class BatteryConsumerViewHolder extends RecyclerView.ViewHolder + implements View.OnClickListener { + private final OnBatteryConsumerSelectedListener mListener; + + public String id; + public TextView titleView; + public TextView detailsView; + public ImageView iconView; + public TextView packagesView; + public TextView powerView; + + BatteryConsumerViewHolder(View view, OnBatteryConsumerSelectedListener listener) { + super(view); + mListener = listener; + view.setOnClickListener(this); + titleView = view.findViewById(android.R.id.title); + detailsView = view.findViewById(R.id.details); + iconView = view.findViewById(android.R.id.icon); + packagesView = view.findViewById(R.id.packages); + powerView = view.findViewById(R.id.power_mah); + powerView.setVisibility(View.VISIBLE); + } + + @Override + public void onClick(View v) { + mListener.onBatteryConsumerSelected(id); + } + } +} diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java new file mode 100644 index 000000000000..4978010f8591 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2020 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.frameworks.core.batterystatsviewer; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.BatteryStats; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.activity.ComponentActivity; +import androidx.activity.result.ActivityResultLauncher; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.loader.app.LoaderManager; +import androidx.loader.app.LoaderManager.LoaderCallbacks; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.internal.os.BatteryStatsHelper; +import com.android.settingslib.utils.AsyncLoaderCompat; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class BatteryStatsViewerActivity extends ComponentActivity { + private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000; + public static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId"; + public static final int LOADER_BATTERY_STATS_HELPER = 0; + public static final int LOADER_BATTERY_USAGE_STATS = 1; + + private BatteryStatsDataAdapter mBatteryStatsDataAdapter; + private Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh; + private SharedPreferences mSharedPref; + private String mBatteryConsumerId; + private TextView mTitleView; + private TextView mDetailsView; + private ImageView mIconView; + private TextView mPackagesView; + private RecyclerView mBatteryConsumerDataView; + private View mLoadingView; + private View mEmptyView; + private ActivityResultLauncher mStartAppPicker = registerForActivityResult( + BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected); + private BatteryStatsHelper mBatteryStatsHelper; + private BatteryUsageStats mBatteryUsageStats; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mSharedPref = getPreferences(Context.MODE_PRIVATE); + + setContentView(R.layout.battery_stats_viewer_layout); + + View appCard = findViewById(R.id.app_card); + appCard.setOnClickListener((e) -> startAppPicker()); + + mTitleView = findViewById(android.R.id.title); + mDetailsView = findViewById(R.id.details); + mIconView = findViewById(android.R.id.icon); + mPackagesView = findViewById(R.id.packages); + + mBatteryConsumerDataView = findViewById(R.id.battery_consumer_data_view); + mBatteryConsumerDataView.setLayoutManager(new LinearLayoutManager(this)); + mBatteryStatsDataAdapter = new BatteryStatsDataAdapter(); + mBatteryConsumerDataView.setAdapter(mBatteryStatsDataAdapter); + + mLoadingView = findViewById(R.id.loading_view); + mEmptyView = findViewById(R.id.empty_view); + + mBatteryConsumerId = mSharedPref.getString(PREF_SELECTED_BATTERY_CONSUMER, null); + loadBatteryStats(); + if (mBatteryConsumerId == null) { + startAppPicker(); + } + } + + @Override + protected void onResume() { + super.onResume(); + periodicBatteryStatsRefresh(); + } + + @Override + protected void onPause() { + super.onPause(); + getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh); + } + + private void startAppPicker() { + mStartAppPicker.launch(null); + } + + private void onApplicationSelected(String batteryConsumerId) { + if (batteryConsumerId == null) { + if (mBatteryConsumerId == null) { + finish(); + } + } else { + mBatteryConsumerId = batteryConsumerId; + mSharedPref.edit() + .putString(PREF_SELECTED_BATTERY_CONSUMER, mBatteryConsumerId) + .apply(); + mLoadingView.setVisibility(View.VISIBLE); + loadBatteryStats(); + } + } + + private void periodicBatteryStatsRefresh() { + loadBatteryStats(); + getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS); + } + + private void loadBatteryStats() { + LoaderManager loaderManager = LoaderManager.getInstance(this); + loaderManager.restartLoader(LOADER_BATTERY_STATS_HELPER, null, + new BatteryStatsHelperLoaderCallbacks()); + loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null, + new BatteryUsageStatsLoaderCallbacks()); + } + + private static class BatteryStatsHelperLoader extends AsyncLoaderCompat { + private final BatteryStatsHelper mBatteryStatsHelper; + private final UserManager mUserManager; + + BatteryStatsHelperLoader(Context context) { + super(context); + mUserManager = context.getSystemService(UserManager.class); + mBatteryStatsHelper = new BatteryStatsHelper(context, + false /* collectBatteryBroadcast */); + mBatteryStatsHelper.create((Bundle) null); + mBatteryStatsHelper.clearStats(); + } + + @Override + public BatteryStatsHelper loadInBackground() { + mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, + UserHandle.myUserId()); + return mBatteryStatsHelper; + } + + @Override + protected void onDiscardResult(BatteryStatsHelper result) { + } + } + + private class BatteryStatsHelperLoaderCallbacks implements LoaderCallbacks { + @NonNull + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryStatsHelperLoader(BatteryStatsViewerActivity.this); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, + BatteryStatsHelper batteryStatsHelper) { + onBatteryStatsHelperLoaded(batteryStatsHelper); + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + } + } + + private static class BatteryUsageStatsLoader extends AsyncLoaderCompat { + private final BatteryStatsManager mBatteryStatsManager; + + BatteryUsageStatsLoader(Context context) { + super(context); + mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class); + } + + @Override + public BatteryUsageStats loadInBackground() { + return mBatteryStatsManager.getBatteryUsageStats(); + } + + @Override + protected void onDiscardResult(BatteryUsageStats result) { + } + } + + private class BatteryUsageStatsLoaderCallbacks implements LoaderCallbacks { + @NonNull + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, + BatteryUsageStats batteryUsageStats) { + onBatteryUsageStatsLoaded(batteryUsageStats); + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + } + } + + public void onBatteryStatsHelperLoaded(BatteryStatsHelper batteryStatsHelper) { + mBatteryStatsHelper = batteryStatsHelper; + onBatteryStatsDataLoaded(); + } + + private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) { + mBatteryUsageStats = batteryUsageStats; + onBatteryStatsDataLoaded(); + } + + public void onBatteryStatsDataLoaded() { + if (mBatteryStatsHelper == null || mBatteryUsageStats == null) { + return; + } + + BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this, mBatteryStatsHelper, + mBatteryUsageStats, mBatteryConsumerId); + + BatteryConsumerInfoHelper.BatteryConsumerInfo + batteryConsumerInfo = batteryConsumerData.getBatteryConsumerInfo(); + if (batteryConsumerInfo == null) { + mTitleView.setText("Battery consumer not found"); + mPackagesView.setVisibility(View.GONE); + } else { + mTitleView.setText(batteryConsumerInfo.label); + if (batteryConsumerInfo.details != null) { + mDetailsView.setText(batteryConsumerInfo.details); + mDetailsView.setVisibility(View.VISIBLE); + } else { + mDetailsView.setVisibility(View.GONE); + } + mIconView.setImageDrawable( + batteryConsumerInfo.iconInfo.loadIcon(getPackageManager())); + + if (batteryConsumerInfo.packages != null) { + mPackagesView.setText(batteryConsumerInfo.packages); + mPackagesView.setVisibility(View.VISIBLE); + } else { + mPackagesView.setVisibility(View.GONE); + } + } + + mBatteryStatsDataAdapter.setEntries(batteryConsumerData.getEntries()); + if (batteryConsumerData.getEntries().isEmpty()) { + mEmptyView.setVisibility(View.VISIBLE); + mBatteryConsumerDataView.setVisibility(View.GONE); + } else { + mEmptyView.setVisibility(View.GONE); + mBatteryConsumerDataView.setVisibility(View.VISIBLE); + } + + mLoadingView.setVisibility(View.GONE); + } + + private static class BatteryStatsDataAdapter extends + RecyclerView.Adapter { + public static class ViewHolder extends RecyclerView.ViewHolder { + public TextView titleTextView; + public TextView amountTextView; + public TextView percentTextView; + + ViewHolder(View itemView) { + super(itemView); + + titleTextView = itemView.findViewById(R.id.title); + amountTextView = itemView.findViewById(R.id.amount); + percentTextView = itemView.findViewById(R.id.percent); + } + } + + private List mEntries = Collections.emptyList(); + + public void setEntries(List entries) { + mEntries = entries; + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return mEntries.size(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { + LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + View itemView = layoutInflater.inflate(R.layout.battery_consumer_entry_layout, parent, + false); + return new ViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { + BatteryConsumerData.Entry entry = mEntries.get(position); + switch (entry.entryType) { + case POWER: + viewHolder.titleTextView.setText(entry.title); + viewHolder.amountTextView.setText( + String.format(Locale.getDefault(), "%.1f mAh", entry.value)); + break; + case DURATION: + viewHolder.titleTextView.setText(entry.title); + viewHolder.amountTextView.setText( + String.format(Locale.getDefault(), "%,d ms", (long) entry.value)); + break; + } + + double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0; + viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%", + proportion)); + } + } +} diff --git a/core/tests/batterystatstests/OWNERS b/core/tests/batterystatstests/OWNERS new file mode 100644 index 000000000000..c22f6a4bbbf6 --- /dev/null +++ b/core/tests/batterystatstests/OWNERS @@ -0,0 +1,2 @@ +include /BATTERY_STATS_OWNERS +include /services/core/java/com/android/server/power/OWNERS diff --git a/core/tests/powertests/OWNERS b/core/tests/powertests/OWNERS deleted file mode 100644 index c22f6a4bbbf6..000000000000 --- a/core/tests/powertests/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include /BATTERY_STATS_OWNERS -include /services/core/java/com/android/server/power/OWNERS diff --git a/core/tests/powertests/PowerStatsLoadTests/Android.bp b/core/tests/powertests/PowerStatsLoadTests/Android.bp deleted file mode 100644 index 66c91adc6540..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/Android.bp +++ /dev/null @@ -1,13 +0,0 @@ -android_test { - name: "PowerStatsLoadTests", - srcs: ["src/**/*.java"], - static_libs: [ - "androidx.test.rules", - "androidx.test.ext.junit", - "compatibility-device-util-axt", - "junit", - ], - libs: ["android.test.runner"], - platform_apis: true, - certificate: "platform", -} diff --git a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml deleted file mode 100644 index 9cecaedf1380..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java deleted file mode 100644 index ca2942647f08..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests; - -import static org.junit.Assert.assertEquals; - -import android.app.Instrumentation; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; - -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.compatibility.common.util.SystemUtil; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public class ConnectivitySetupRule implements TestRule { - - private final boolean mWifiEnabled; - private final ConnectivityManager mConnectivityManager; - private final WifiManager mWifiManager; - private boolean mInitialWifiState; - - public ConnectivitySetupRule(boolean wifiEnabled) { - mWifiEnabled = wifiEnabled; - - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - Context context = instrumentation.getContext(); - mConnectivityManager = context.getSystemService(ConnectivityManager.class); - mWifiManager = context.getSystemService(WifiManager.class); - } - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - mInitialWifiState = isWiFiConnected(); - setWiFiState(mWifiEnabled); - base.evaluate(); - } finally { - setWiFiState(mInitialWifiState); - } - } - }; - } - - private void setWiFiState(final boolean enable) throws InterruptedException { - boolean wiFiConnected = isWiFiConnected(); - if (enable == wiFiConnected) { - return; - } - - NetworkTracker tracker = new NetworkTracker(!mWifiEnabled); - mConnectivityManager.registerNetworkCallback( - new NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED).build(), - tracker); - - if (enable) { - SystemUtil.runShellCommand("svc wifi enable"); - //noinspection deprecation - SystemUtil.runWithShellPermissionIdentity(mWifiManager::reconnect, - android.Manifest.permission.NETWORK_SETTINGS); - } else { - SystemUtil.runShellCommand("svc wifi disable"); - } - - tracker.waitForExpectedState(); - - assertEquals("Wifi must be " + (enable ? "connected to" : "disconnected from") - + " an access point for this test.", enable, isWiFiConnected()); - - mConnectivityManager.unregisterNetworkCallback(tracker); - } - - private boolean isWiFiConnected() { - return mWifiManager.isWifiEnabled() && mConnectivityManager.getActiveNetwork() != null - && !mConnectivityManager.isActiveNetworkMetered(); - } - - private class NetworkTracker extends ConnectivityManager.NetworkCallback { - private static final int MSG_CHECK_ACTIVE_NETWORK = 1; - - private final CountDownLatch mReceiveLatch = new CountDownLatch(1); - - private final boolean mExpectedMetered; - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_CHECK_ACTIVE_NETWORK) { - checkActiveNetwork(); - } - } - }; - - private NetworkTracker(boolean expectedMetered) { - mExpectedMetered = expectedMetered; - } - - @Override - public void onAvailable(Network network, NetworkCapabilities networkCapabilities, - LinkProperties linkProperties, boolean blocked) { - checkActiveNetwork(); - } - - @Override - public void onLost(Network network) { - checkActiveNetwork(); - } - - boolean waitForExpectedState() throws InterruptedException { - checkActiveNetwork(); - return mReceiveLatch.await(60, TimeUnit.SECONDS); - } - - private void checkActiveNetwork() { - if (mReceiveLatch.getCount() == 0) { - return; - } - - if (mConnectivityManager.getActiveNetwork() != null - && mConnectivityManager.isActiveNetworkMetered() == mExpectedMetered) { - mReceiveLatch.countDown(); - } else { - mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000); - } - } - } -} diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java deleted file mode 100644 index 88cb719add60..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests; - -import android.os.Process; - -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; - -import java.util.ArrayList; -import java.util.List; - -public class PowerMetrics { - private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; - private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; - private static final String PACKAGE_SYSTEMUI = "com.android.systemui"; - private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, - PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; - - enum MetricKind { - POWER, - DURATION, - } - - public static final String METRIC_APP_POWER = "appPower"; - public static final String METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL = "appPowerExcludeSystem"; - public static final String METRIC_APP_POWER_EXCLUDE_SMEARED = "appPowerExcludeSmeared"; - public static final String METRIC_SCREEN_POWER = "screenPower"; - public static final String METRIC_WIFI_POWER = "wifiPower"; - public static final String METRIC_SYSTEM_SERVICE_CPU_POWER = "systemService"; - public static final String METRIC_OTHER_POWER = "otherPower"; - public static final String METRIC_CPU_POWER = "cpuPower"; - public static final String METRIC_RAM_POWER = "ramPower"; - public static final String METRIC_WAKELOCK_POWER = "wakelockPower"; - public static final String METRIC_MOBILE_RADIO_POWER = "mobileRadioPower"; - public static final String METRIC_BLUETOOTH_POWER = "bluetoothPower"; - public static final String METRIC_GPS_POWER = "gpsPower"; - public static final String METRIC_CAMERA_POWER = "cameraPower"; - public static final String METRIC_FLASHLIGHT_POWER = "flashlightPower"; - public static final String METRIC_SENSORS_POWER = "sensorsPower"; - public static final String METRIC_AUDIO_POWER = "audioPower"; - public static final String METRIC_VIDEO_POWER = "videoPower"; - public static final String METRIC_CPU_TIME = "cpuTime"; - public static final String METRIC_CPU_FOREGROUND_TIME = "cpuForegroundTime"; - public static final String METRIC_WAKELOCK_TIME = "wakelockTime"; - public static final String METRIC_WIFI_RUNNING_TIME = "wifiRunningTime"; - public static final String METRIC_BLUETOOTH_RUNNING_TIME = "bluetoothRunningTime"; - public static final String METRIC_GPS_TIME = "gpsTime"; - public static final String METRIC_CAMERA_TIME = "cameraTime"; - public static final String METRIC_FLASHLIGHT_TIME = "flashlightTime"; - public static final String METRIC_AUDIO_TIME = "audioTime"; - public static final String METRIC_VIDEO_TIME = "videoTime"; - - public static class Metric { - public String metricType; - public MetricKind metricKind; - public String title; - public double value; - public double total; - } - - private final double mMinDrainedPower; - private final double mMaxDrainedPower; - - private List mMetrics = new ArrayList<>(); - - public PowerMetrics(BatteryStatsHelper batteryStatsHelper, int uid) { - mMinDrainedPower = batteryStatsHelper.getMinDrainedPower(); - mMaxDrainedPower = batteryStatsHelper.getMaxDrainedPower(); - - List usageList = batteryStatsHelper.getUsageList(); - - double totalPowerMah = 0; - double totalSmearedPowerMah = 0; - double totalPowerExcludeSystemMah = 0; - double totalScreenPower = 0; - double totalProportionalSmearMah = 0; - double totalCpuPowerMah = 0; - double totalSystemServiceCpuPowerMah = 0; - double totalUsagePowerMah = 0; - double totalWakeLockPowerMah = 0; - double totalMobileRadioPowerMah = 0; - double totalWifiPowerMah = 0; - double totalBluetoothPowerMah = 0; - double totalGpsPowerMah = 0; - double totalCameraPowerMah = 0; - double totalFlashlightPowerMah = 0; - double totalSensorPowerMah = 0; - double totalAudioPowerMah = 0; - double totalVideoPowerMah = 0; - - long totalCpuTimeMs = 0; - long totalCpuFgTimeMs = 0; - long totalWakeLockTimeMs = 0; - long totalWifiRunningTimeMs = 0; - long totalBluetoothRunningTimeMs = 0; - long totalGpsTimeMs = 0; - long totalCameraTimeMs = 0; - long totalFlashlightTimeMs = 0; - long totalAudioTimeMs = 0; - long totalVideoTimeMs = 0; - - BatterySipper uidSipper = null; - for (BatterySipper sipper : usageList) { - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - totalScreenPower = sipper.sumPower(); - } - - if (isHiddenDrainType(sipper.drainType)) { - continue; - } - - if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) { - uidSipper = sipper; - } - - totalPowerMah += sipper.sumPower(); - totalSmearedPowerMah += sipper.totalSmearedPowerMah; - totalProportionalSmearMah += sipper.proportionalSmearMah; - - if (!isSystemSipper(sipper)) { - totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah; - } - - totalCpuPowerMah += sipper.cpuPowerMah; - totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah; - totalUsagePowerMah += sipper.usagePowerMah; - totalWakeLockPowerMah += sipper.wakeLockPowerMah; - totalMobileRadioPowerMah += sipper.mobileRadioPowerMah; - totalWifiPowerMah += sipper.wifiPowerMah; - totalBluetoothPowerMah += sipper.bluetoothPowerMah; - totalGpsPowerMah += sipper.gpsPowerMah; - totalCameraPowerMah += sipper.cameraPowerMah; - totalFlashlightPowerMah += sipper.flashlightPowerMah; - totalSensorPowerMah += sipper.sensorPowerMah; - totalAudioPowerMah += sipper.audioPowerMah; - totalVideoPowerMah += sipper.videoPowerMah; - - totalCpuTimeMs += sipper.cpuTimeMs; - totalCpuFgTimeMs += sipper.cpuFgTimeMs; - totalWakeLockTimeMs += sipper.wakeLockTimeMs; - totalWifiRunningTimeMs += sipper.wifiRunningTimeMs; - totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs; - totalGpsTimeMs += sipper.gpsTimeMs; - totalCameraTimeMs += sipper.cameraTimeMs; - totalFlashlightTimeMs += sipper.flashlightTimeMs; - totalAudioTimeMs += sipper.audioTimeMs; - totalVideoTimeMs += sipper.videoTimeMs; - } - - if (uidSipper == null) { - return; - } - - addMetric(METRIC_APP_POWER, MetricKind.POWER, "Total power", - uidSipper.totalSmearedPowerMah, totalSmearedPowerMah); - addMetric(METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL, MetricKind.POWER, - "Total power excluding system", - uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah); - addMetric(METRIC_SCREEN_POWER, MetricKind.POWER, "Screen, smeared", - uidSipper.screenPowerMah, totalScreenPower); - addMetric(METRIC_OTHER_POWER, MetricKind.POWER, "Other, smeared", - uidSipper.proportionalSmearMah, totalProportionalSmearMah); - addMetric(METRIC_APP_POWER_EXCLUDE_SMEARED, MetricKind.POWER, "Excluding smeared", - uidSipper.totalPowerMah, totalPowerMah); - addMetric(METRIC_CPU_POWER, MetricKind.POWER, "CPU", - uidSipper.cpuPowerMah, totalCpuPowerMah); - addMetric(METRIC_SYSTEM_SERVICE_CPU_POWER, MetricKind.POWER, "System services", - uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah); - addMetric(METRIC_RAM_POWER, MetricKind.POWER, "RAM", - uidSipper.usagePowerMah, totalUsagePowerMah); - addMetric(METRIC_WAKELOCK_POWER, MetricKind.POWER, "Wake lock", - uidSipper.wakeLockPowerMah, totalWakeLockPowerMah); - addMetric(METRIC_MOBILE_RADIO_POWER, MetricKind.POWER, "Mobile radio", - uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah); - addMetric(METRIC_WIFI_POWER, MetricKind.POWER, "WiFi", - uidSipper.wifiPowerMah, totalWifiPowerMah); - addMetric(METRIC_BLUETOOTH_POWER, MetricKind.POWER, "Bluetooth", - uidSipper.bluetoothPowerMah, totalBluetoothPowerMah); - addMetric(METRIC_GPS_POWER, MetricKind.POWER, "GPS", - uidSipper.gpsPowerMah, totalGpsPowerMah); - addMetric(METRIC_CAMERA_POWER, MetricKind.POWER, "Camera", - uidSipper.cameraPowerMah, totalCameraPowerMah); - addMetric(METRIC_FLASHLIGHT_POWER, MetricKind.POWER, "Flashlight", - uidSipper.flashlightPowerMah, totalFlashlightPowerMah); - addMetric(METRIC_SENSORS_POWER, MetricKind.POWER, "Sensors", - uidSipper.sensorPowerMah, totalSensorPowerMah); - addMetric(METRIC_AUDIO_POWER, MetricKind.POWER, "Audio", - uidSipper.audioPowerMah, totalAudioPowerMah); - addMetric(METRIC_VIDEO_POWER, MetricKind.POWER, "Video", - uidSipper.videoPowerMah, totalVideoPowerMah); - - addMetric(METRIC_CPU_TIME, MetricKind.DURATION, "CPU time", - uidSipper.cpuTimeMs, totalCpuTimeMs); - addMetric(METRIC_CPU_FOREGROUND_TIME, MetricKind.DURATION, "CPU foreground time", - uidSipper.cpuFgTimeMs, totalCpuFgTimeMs); - addMetric(METRIC_WAKELOCK_TIME, MetricKind.DURATION, "Wake lock time", - uidSipper.wakeLockTimeMs, totalWakeLockTimeMs); - addMetric(METRIC_WIFI_RUNNING_TIME, MetricKind.DURATION, "WiFi running time", - uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs); - addMetric(METRIC_BLUETOOTH_RUNNING_TIME, MetricKind.DURATION, "Bluetooth time", - uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs); - addMetric(METRIC_GPS_TIME, MetricKind.DURATION, "GPS time", - uidSipper.gpsTimeMs, totalGpsTimeMs); - addMetric(METRIC_CAMERA_TIME, MetricKind.DURATION, "Camera time", - uidSipper.cameraTimeMs, totalCameraTimeMs); - addMetric(METRIC_FLASHLIGHT_TIME, MetricKind.DURATION, "Flashlight time", - uidSipper.flashlightTimeMs, totalFlashlightTimeMs); - addMetric(METRIC_AUDIO_TIME, MetricKind.DURATION, "Audio time", - uidSipper.audioTimeMs, totalAudioTimeMs); - addMetric(METRIC_VIDEO_TIME, MetricKind.DURATION, "Video time", - uidSipper.videoTimeMs, totalVideoTimeMs); - } - - public List getMetrics() { - return mMetrics; - } - - public double getMinDrainedPower() { - return mMinDrainedPower; - } - - public double getMaxDrainedPower() { - return mMaxDrainedPower; - } - - protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) { - return drainType == BatterySipper.DrainType.IDLE - || drainType == BatterySipper.DrainType.CELL - || drainType == BatterySipper.DrainType.SCREEN - || drainType == BatterySipper.DrainType.UNACCOUNTED - || drainType == BatterySipper.DrainType.OVERCOUNTED - || drainType == BatterySipper.DrainType.BLUETOOTH - || drainType == BatterySipper.DrainType.WIFI; - } - - private boolean isSystemSipper(BatterySipper sipper) { - final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); - if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { - return true; - } else if (sipper.mPackages != null) { - for (final String packageName : sipper.mPackages) { - for (final String systemPackage : PACKAGES_SYSTEM) { - if (systemPackage.equals(packageName)) { - return true; - } - } - } - } - - return false; - } - - private void addMetric(String metricType, MetricKind metricKind, String title, double amount, - double totalAmount) { - Metric metric = new Metric(); - metric.metricType = metricType; - metric.metricKind = metricKind; - metric.title = title; - metric.value = amount; - metric.total = totalAmount; - mMetrics.add(metric); - } -} diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java deleted file mode 100644 index a71559b5ad6b..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.app.Instrumentation; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.BatteryStats; -import android.os.Bundle; -import android.os.Process; -import android.os.SystemClock; -import android.os.UserManager; -import android.util.Log; -import android.util.TimeUtils; - -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.compatibility.common.util.SystemUtil; -import com.android.internal.os.BatteryStatsHelper; -import com.android.internal.os.LoggingPrintStream; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public class PowerMetricsCollector implements TestRule { - private final String mTag; - private final float mBatteryDrainThresholdPct; - private final int mTimeoutMillis; - - private final Context mContext; - private final UserManager mUserManager; - private final int mUid; - private final BatteryStatsHelper mStatsHelper; - private final CountDownLatch mSuspendingBatteryInput = new CountDownLatch(1); - - private long mStartTime; - private volatile float mInitialBatteryLevel; - private volatile float mCurrentBatteryLevel; - private int mIterations; - private PowerMetrics mInitialPowerMetrics; - private PowerMetrics mFinalPowerMetrics; - private List mPowerMetricsDelta; - private Intent mBatteryStatus; - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - BroadcastReceiver batteryBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - handleBatteryStatus(intent); - } - }; - mBatteryStatus = mContext.registerReceiver(batteryBroadcastReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - disableCharger(); - try { - prepareBatteryLevelMonitor(); - mStartTime = SystemClock.uptimeMillis(); - base.evaluate(); - captureFinalPowerStatsData(); - } finally { - mContext.unregisterReceiver(batteryBroadcastReceiver); - enableCharger(); - } - } - }; - } - - public PowerMetricsCollector(String tag, float batteryDrainThresholdPct, int timeoutMillis) { - mTag = tag; - mBatteryDrainThresholdPct = batteryDrainThresholdPct; - mTimeoutMillis = timeoutMillis; - - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - mContext = instrumentation.getContext(); - mUid = Process.myUid(); - mUserManager = mContext.getSystemService(UserManager.class); - mStatsHelper = new BatteryStatsHelper(mContext, false /* collectBatteryBroadcast */); - mStatsHelper.create((Bundle) null); - } - - private void disableCharger() throws InterruptedException { - SystemUtil.runShellCommand("dumpsys battery suspend_input"); - final boolean success = mSuspendingBatteryInput.await(10, TimeUnit.SECONDS); - assertTrue("Timed out waiting for battery input to be suspended", success); - } - - private void enableCharger() { - SystemUtil.runShellCommand("dumpsys battery reset"); - } - - private PowerMetrics readBatteryStatsData() { - mStatsHelper.clearStats(); - mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, - mUserManager.getUserProfiles()); - return new PowerMetrics(mStatsHelper, mUid); - } - - protected void prepareBatteryLevelMonitor() { - handleBatteryStatus(mBatteryStatus); - mInitialBatteryLevel = mCurrentBatteryLevel; - } - - protected void handleBatteryStatus(Intent intent) { - if (mFinalPowerMetrics != null) { - return; - } - - final boolean isCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0; - - if (mSuspendingBatteryInput.getCount() > 0) { - if (!isCharging) { - mSuspendingBatteryInput.countDown(); - } - return; - } - - if (isCharging) { - fail("Device must remain disconnected from the power source " - + "for the duration of the test"); - } - - int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); - - mCurrentBatteryLevel = level * 100 / (float) scale; - Log.i(mTag, "Battery level = " + mCurrentBatteryLevel); - - // We delay tracking until the battery level drops. If the resolution of - // battery level is 1%, and the initially reported level is 73, we don't know whether - // it's 73.1 or 73.7. Once it drops to 72, we can be confident that the real battery - // level it is very close to 72.0 and can start tracking. - if (mInitialPowerMetrics == null && mCurrentBatteryLevel < mInitialBatteryLevel) { - mInitialBatteryLevel = mCurrentBatteryLevel; - mInitialPowerMetrics = readBatteryStatsData(); - } - } - - private void captureFinalPowerStatsData() { - if (mFinalPowerMetrics != null) { - return; - } - - mFinalPowerMetrics = readBatteryStatsData(); - - mPowerMetricsDelta = new ArrayList<>(); - List initialPowerMetrics = mInitialPowerMetrics.getMetrics(); - List finalPowerMetrics = mFinalPowerMetrics.getMetrics(); - for (PowerMetrics.Metric initialMetric : initialPowerMetrics) { - PowerMetrics.Metric finalMetric = null; - for (PowerMetrics.Metric metric : finalPowerMetrics) { - if (metric.title.equals(initialMetric.title)) { - finalMetric = metric; - break; - } - } - - if (finalMetric != null) { - PowerMetrics.Metric delta = new PowerMetrics.Metric(); - delta.metricType = initialMetric.metricType; - delta.metricKind = initialMetric.metricKind; - delta.title = initialMetric.title; - delta.total = finalMetric.total - initialMetric.total; - delta.value = finalMetric.value - initialMetric.value; - mPowerMetricsDelta.add(delta); - } - } - } - - /** - * Returns false if sufficient data has been accumulated. - */ - public boolean checkpoint() { - long elapsedTime = SystemClock.uptimeMillis() - mStartTime; - if (elapsedTime >= mTimeoutMillis) { - Log.i(mTag, "Timeout reached " + TimeUtils.formatDuration(elapsedTime)); - captureFinalPowerStatsData(); - return false; - } - - if (mInitialPowerMetrics == null) { - return true; - } - - if (mInitialBatteryLevel - mCurrentBatteryLevel >= mBatteryDrainThresholdPct) { - Log.i(mTag, - "Battery drain reached " + (mInitialBatteryLevel - mCurrentBatteryLevel) + "%"); - captureFinalPowerStatsData(); - return false; - } - - mIterations++; - return true; - } - - - public int getIterationCount() { - return mIterations; - } - - public void dumpMetrics() { - dumpMetrics(new LoggingPrintStream() { - @Override - protected void log(String line) { - Log.i(mTag, line); - } - }); - } - - public void dumpMetrics(PrintStream out) { - List initialPowerMetrics = mInitialPowerMetrics.getMetrics(); - List finalPowerMetrics = mFinalPowerMetrics.getMetrics(); - - out.println("== Power metrics at test start"); - dumpPowerStatsData(out, initialPowerMetrics); - - out.println("== Power metrics at test end"); - dumpPowerStatsData(out, finalPowerMetrics); - - out.println("== Power metrics delta"); - dumpPowerStatsData(out, mPowerMetricsDelta); - } - - protected void dumpPowerStatsData(PrintStream out, List metrics) { - Locale locale = Locale.getDefault(); - for (PowerMetrics.Metric metric : metrics) { - double proportion = metric.total != 0 ? metric.value * 100 / metric.total : 0; - switch (metric.metricKind) { - case POWER: - out.println( - String.format(locale, " %-30s %7.1f mAh %4.1f%%", metric.title, - metric.value, proportion)); - break; - case DURATION: - out.println( - String.format(locale, " %-30s %,7d ms %4.1f%%", metric.title, - (long) metric.value, proportion)); - break; - } - } - } - - public void dumpMetricAsPercentageOfDrainedPower(String metricType) { - double minDrainedPower = - mFinalPowerMetrics.getMinDrainedPower() - mInitialPowerMetrics.getMinDrainedPower(); - double maxDrainedPower = - mFinalPowerMetrics.getMaxDrainedPower() - mInitialPowerMetrics.getMaxDrainedPower(); - - PowerMetrics.Metric metric = getMetric(metricType); - double metricDelta = metric.value; - - if (maxDrainedPower - minDrainedPower < 0.1f) { - Log.i(mTag, String.format(Locale.getDefault(), - "%s power consumed by the test: %.1f of %.1f mAh (%.1f%%)", - metric.title, metricDelta, maxDrainedPower, - metricDelta / maxDrainedPower * 100)); - } else { - Log.i(mTag, String.format(Locale.getDefault(), - "%s power consumed by the test: %.1f of %.1f - %.1f mAh (%.1f%% - %.1f%%)", - metric.title, metricDelta, minDrainedPower, maxDrainedPower, - metricDelta / minDrainedPower * 100, metricDelta / maxDrainedPower * 100)); - } - } - - public PowerMetrics.Metric getMetric(String metricType) { - for (PowerMetrics.Metric metric : mPowerMetricsDelta) { - if (metric.metricType.equals(metricType)) { - return metric; - } - } - return null; - } -} diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java deleted file mode 100644 index 911ccba3ac78..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests; - -import static org.junit.Assert.assertNotNull; - -import android.app.Instrumentation; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.util.Log; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -public class SystemServiceCallLoadTest { - private static final String TAG = "SystemServiceCallLoadTest"; - private static final int TIMEOUT_MILLIS = 60 * 60 * 1000; - private static final float BATTERY_DRAIN_THRESHOLD_PCT = 2.99f; - - @Rule - public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG, - BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS); - - private PackageManager mPackageManager; - - @Before - public void setup() { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - mPackageManager = instrumentation.getContext().getPackageManager(); - } - - @Test - public void test() { - while (mPowerMetricsCollector.checkpoint()) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.parse("http://example.com/"), "text/plain"); - intent.addCategory(Intent.CATEGORY_BROWSABLE); - ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); - assertNotNull(resolveInfo); - } - - mPowerMetricsCollector.dumpMetrics(); - - Log.i(TAG, "=="); - Log.i(TAG, "Total system server calls made " + mPowerMetricsCollector.getIterationCount()); - - mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower( - PowerMetrics.METRIC_SYSTEM_SERVICE_CPU_POWER); - } -} diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java deleted file mode 100644 index 90627192946d..000000000000 --- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests; - -import android.util.Log; - -import org.junit.Rule; -import org.junit.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -public class WiFiLoadTest { - private static final String TAG = "WiFiLoadTest"; - private static final String DOWNLOAD_TEST_URL = - "https://i.ytimg.com/vi/l5mE3Tpjejs/maxresdefault.jpg"; - - private static final int TIMEOUT_MILLIS = 60 * 60 * 1000; - private static final float BATTERY_DRAIN_THRESHOLD_PCT = 0.99f; - - @Rule - public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG, - BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS); - - @Rule - public ConnectivitySetupRule mConnectivitySetupRule = - new ConnectivitySetupRule(/* WiFi enabled */true); - - @Test - public void test() throws IOException { - long totalBytesRead = 0; - URL url = new URL(DOWNLOAD_TEST_URL); - byte[] buffer = new byte[131072]; // Large buffer to minimize CPU usage - - while (mPowerMetricsCollector.checkpoint()) { - try (InputStream inputStream = url.openStream()) { - while (true) { - int count = inputStream.read(buffer); - if (count < 0) { - break; - } - totalBytesRead += count; - } - } - } - - mPowerMetricsCollector.dumpMetrics(); - - Log.i(TAG, "=="); - Log.i(TAG, "WiFi running time: " + (long) mPowerMetricsCollector.getMetric( - PowerMetrics.METRIC_WIFI_RUNNING_TIME).value); - Log.i(TAG, "Total bytes read over WiFi: " + totalBytesRead); - - mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower( - PowerMetrics.METRIC_WIFI_POWER); - } -} diff --git a/core/tests/powertests/PowerStatsViewer/Android.bp b/core/tests/powertests/PowerStatsViewer/Android.bp deleted file mode 100644 index a3dc4fb4ff74..000000000000 --- a/core/tests/powertests/PowerStatsViewer/Android.bp +++ /dev/null @@ -1,13 +0,0 @@ -android_test { - name: "PowerStatsViewer", - srcs: ["src/**/*.java"], - defaults: ["SettingsLibDefaults"], - static_libs: [ - "androidx.appcompat_appcompat", - "androidx.cardview_cardview", - "androidx.recyclerview_recyclerview", - "com.google.android.material_material", - ], - platform_apis: true, - certificate: "platform", -} diff --git a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml deleted file mode 100644 index 28ea05fca61e..000000000000 --- a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml deleted file mode 100644 index fbd0ebd61bc7..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_info_layout.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml deleted file mode 100644 index ecc89f0cb33e..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_activity_layout.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml deleted file mode 100644 index bea38c18c20b..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/layout/power_consumer_picker_layout.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml deleted file mode 100644 index 1ced825adf31..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml deleted file mode 100644 index 238e238deeaa..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/tests/powertests/PowerStatsViewer/res/values/styles.xml b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml deleted file mode 100644 index 629d729e7b9a..000000000000 --- a/core/tests/powertests/PowerStatsViewer/res/values/styles.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java deleted file mode 100644 index 6fec2405b0c6..000000000000 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerInfoHelper.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsviewer; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Process; - -import androidx.annotation.NonNull; - -import com.android.internal.os.BatterySipper; - -import java.util.Locale; - -class PowerConsumerInfoHelper { - - private static final String SYSTEM_SERVER_PACKAGE_NAME = "android"; - - public static class PowerConsumerInfo { - public String id; - public CharSequence label; - public double powerMah; - public ApplicationInfo iconInfo; - public CharSequence packages; - public CharSequence details; - } - - @NonNull - public static PowerConsumerInfo makePowerConsumerInfo(PackageManager packageManager, - @NonNull BatterySipper sipper) { - PowerConsumerInfo info = new PowerConsumerInfo(); - info.id = PowerStatsData.powerConsumerId(sipper); - sipper.sumPower(); - info.powerMah = sipper.totalSmearedPowerMah; - switch (sipper.drainType) { - case APP: { - int uid = sipper.getUid(); - info.details = String.format("UID: %d", uid); - String packageWithHighestDrain = sipper.packageWithHighestDrain; - if (uid == Process.ROOT_UID) { - info.label = ""; - } else { - String[] packages = packageManager.getPackagesForUid(uid); - String primaryPackageName = null; - if (uid == Process.SYSTEM_UID) { - primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME; - } else if (packages != null) { - for (String name : packages) { - primaryPackageName = name; - if (name.equals(packageWithHighestDrain)) { - break; - } - } - } - - if (primaryPackageName != null) { - try { - ApplicationInfo applicationInfo = - packageManager.getApplicationInfo(primaryPackageName, 0); - info.label = applicationInfo.loadLabel(packageManager); - info.iconInfo = applicationInfo; - } catch (PackageManager.NameNotFoundException e) { - info.label = primaryPackageName; - } - } else if (packageWithHighestDrain != null) { - info.label = packageWithHighestDrain; - } - - if (packages != null && packages.length > 0) { - StringBuilder sb = new StringBuilder(); - if (primaryPackageName != null) { - sb.append(primaryPackageName); - } - for (String packageName : packages) { - if (packageName.equals(primaryPackageName)) { - continue; - } - - if (sb.length() != 0) { - sb.append(", "); - } - sb.append(packageName); - } - - info.packages = sb; - } - } - break; - } - case USER: - info.label = "User"; - info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId); - break; - case AMBIENT_DISPLAY: - info.label = "Ambient display"; - break; - case BLUETOOTH: - info.label = "Bluetooth"; - break; - case CAMERA: - info.label = "Camera"; - break; - case CELL: - info.label = "Cell"; - break; - case FLASHLIGHT: - info.label = "Flashlight"; - break; - case IDLE: - info.label = "Idle"; - break; - case MEMORY: - info.label = "Memory"; - break; - case OVERCOUNTED: - info.label = "Overcounted"; - break; - case PHONE: - info.label = "Phone"; - break; - case SCREEN: - info.label = "Screen"; - break; - case UNACCOUNTED: - info.label = "Unaccounted"; - break; - case WIFI: - info.label = "WiFi"; - break; - } - // Default the app icon to System Server. This includes root, dex2oat and other UIDs. - if (info.iconInfo == null) { - try { - info.iconInfo = - packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0); - } catch (PackageManager.NameNotFoundException nameNotFoundException) { - // Won't happen - } - } - return info; - } -} diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java deleted file mode 100644 index f56d113980c8..000000000000 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerActivity.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsviewer; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import androidx.activity.result.contract.ActivityResultContract; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; - -import com.google.android.material.tabs.TabLayout; - -/** - * Picker, showing a sorted lists of applications and other types of entities consuming power. - * Returns the selected entity ID or null. - */ -public class PowerConsumerPickerActivity extends FragmentActivity { - - public static final ActivityResultContract CONTRACT = - new ActivityResultContract() { - @NonNull - @Override - public Intent createIntent(@NonNull Context context, Void aVoid) { - return new Intent(context, PowerConsumerPickerActivity.class); - } - - @Override - public String parseResult(int resultCode, @Nullable Intent intent) { - if (resultCode != RESULT_OK || intent == null) { - return null; - } - return intent.getStringExtra(Intent.EXTRA_RETURN_RESULT); - } - }; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - getActionBar().setDisplayHomeAsUpEnabled(true); - - setContentView(R.layout.power_consumer_picker_activity_layout); - - ViewPager viewPager = findViewById(R.id.pager); - - FragmentStatePagerAdapter adapter = new FragmentStatePagerAdapter( - getSupportFragmentManager()) { - - @Override - public int getCount() { - return 2; - } - - @NonNull - @Override - public Fragment getItem(int position) { - switch (position) { - case 0: - return new PowerConsumerPickerFragment( - PowerConsumerPickerFragment.PICKER_TYPE_APP); - case 1: - default: - return new PowerConsumerPickerFragment( - PowerConsumerPickerFragment.PICKER_TYPE_DRAIN); - } - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return "Apps"; - case 1: - return "Drains"; - } - return null; - } - }; - - viewPager.setAdapter(adapter); - TabLayout tabLayout = findViewById(R.id.tab_layout); - tabLayout.setupWithViewPager(viewPager); - } - - public void setSelectedPowerConsumer(String id) { - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_RETURN_RESULT, id); - setResult(RESULT_OK, intent); - finish(); - } - - @Override - public boolean onNavigateUp() { - onBackPressed(); - return true; - } -} diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java deleted file mode 100644 index 25225b87f602..000000000000 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerConsumerPickerFragment.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2008 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.frameworks.core.powerstatsviewer; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.BatteryStats; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.frameworks.core.powerstatsviewer.PowerConsumerInfoHelper.PowerConsumerInfo; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; -import com.android.settingslib.utils.AsyncLoaderCompat; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; - -/** - * Picker, showing a sorted lists of applications or other types of entities consuming power. - * Returns the selected entity ID or null. - */ -public class PowerConsumerPickerFragment extends Fragment { - private static final String TAG = "AppPicker"; - - public static final String PICKER_TYPE = "pickertype"; - - public static final int PICKER_TYPE_APP = 0; - public static final int PICKER_TYPE_DRAIN = 1; - - private PowerConsumerListAdapter mPowerConsumerListAdapter; - private RecyclerView mAppList; - private View mLoadingView; - - private interface OnPowerConsumerSelectedListener { - void onPowerConsumerSelected(String uid); - } - - public PowerConsumerPickerFragment(int pickerType) { - Bundle args = new Bundle(); - args.putInt(PICKER_TYPE, pickerType); - setArguments(args); - } - - public PowerConsumerPickerFragment() { - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.power_consumer_picker_layout, container, false); - mLoadingView = view.findViewById(R.id.loading_view); - - mAppList = view.findViewById(R.id.list_view); - mAppList.setLayoutManager(new LinearLayoutManager(getContext())); - mPowerConsumerListAdapter = new PowerConsumerListAdapter( - PowerConsumerPickerFragment.this::setSelectedPowerConsumer); - mAppList.setAdapter(mPowerConsumerListAdapter); - - LoaderManager.getInstance(this).initLoader(0, getArguments(), - new PowerConsumerListLoaderCallbacks()); - return view; - } - - public void setSelectedPowerConsumer(String id) { - ((PowerConsumerPickerActivity) getActivity()).setSelectedPowerConsumer(id); - } - - private static class PowerConsumerListLoader extends - AsyncLoaderCompat> { - private final BatteryStatsHelper mStatsHelper; - private final int mPickerType; - private final UserManager mUserManager; - private final PackageManager mPackageManager; - - PowerConsumerListLoader(Context context, int pickerType) { - super(context); - mUserManager = context.getSystemService(UserManager.class); - mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */); - mPickerType = pickerType; - mStatsHelper.create((Bundle) null); - mStatsHelper.clearStats(); - mPackageManager = context.getPackageManager(); - } - - @Override - public List loadInBackground() { - List powerConsumerList = new ArrayList<>(); - - mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); - - final List usageList = mStatsHelper.getUsageList(); - for (BatterySipper sipper : usageList) { - switch (mPickerType) { - case PICKER_TYPE_APP: - if (sipper.drainType != BatterySipper.DrainType.APP) { - continue; - } - break; - case PICKER_TYPE_DRAIN: - default: - if (sipper.drainType == BatterySipper.DrainType.APP) { - continue; - } - } - - powerConsumerList.add( - PowerConsumerInfoHelper.makePowerConsumerInfo(mPackageManager, sipper)); - } - - powerConsumerList.sort( - Comparator.comparing((PowerConsumerInfo a) -> a.powerMah).reversed()); - return powerConsumerList; - } - - @Override - protected void onDiscardResult(List result) { - } - } - - private class PowerConsumerListLoaderCallbacks implements - LoaderManager.LoaderCallbacks> { - - @NonNull - @Override - public Loader> onCreateLoader(int id, Bundle args) { - return new PowerConsumerListLoader(getContext(), args.getInt(PICKER_TYPE)); - } - - @Override - public void onLoadFinished(@NonNull Loader> loader, - List powerConsumerList) { - mPowerConsumerListAdapter.setPowerConsumerList(powerConsumerList); - mAppList.setVisibility(View.VISIBLE); - mLoadingView.setVisibility(View.GONE); - } - - @Override - public void onLoaderReset( - @NonNull Loader> loader) { - } - } - - public class PowerConsumerListAdapter extends RecyclerView.Adapter { - private final OnPowerConsumerSelectedListener mListener; - private List mPowerConsumerList; - - public PowerConsumerListAdapter(OnPowerConsumerSelectedListener listener) { - mListener = listener; - } - - void setPowerConsumerList(List powerConsumerList) { - mPowerConsumerList = powerConsumerList; - notifyDataSetChanged(); - } - - @Override - public int getItemCount() { - return mPowerConsumerList.size(); - } - - @NonNull - @Override - public PowerConsumerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, - int position) { - LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext()); - View view = layoutInflater.inflate(R.layout.power_consumer_info_layout, viewGroup, - false); - return new PowerConsumerViewHolder(view, mListener); - } - - @Override - public void onBindViewHolder(@NonNull PowerConsumerViewHolder viewHolder, int position) { - PowerConsumerInfoHelper.PowerConsumerInfo item = mPowerConsumerList.get(position); - viewHolder.id = item.id; - viewHolder.titleView.setText(item.label); - if (item.details != null) { - viewHolder.detailsView.setText(item.details); - viewHolder.detailsView.setVisibility(View.VISIBLE); - } else { - viewHolder.detailsView.setVisibility(View.GONE); - } - viewHolder.powerView.setText( - String.format(Locale.getDefault(), "%.1f mAh", item.powerMah)); - viewHolder.iconView.setImageDrawable( - item.iconInfo.loadIcon(getContext().getPackageManager())); - if (item.packages != null) { - viewHolder.packagesView.setText(item.packages); - viewHolder.packagesView.setVisibility(View.VISIBLE); - } else { - viewHolder.packagesView.setVisibility(View.GONE); - } - } - } - - // View Holder used when displaying apps - public static class PowerConsumerViewHolder extends RecyclerView.ViewHolder - implements View.OnClickListener { - private final OnPowerConsumerSelectedListener mListener; - - public String id; - public TextView titleView; - public TextView detailsView; - public ImageView iconView; - public TextView packagesView; - public TextView powerView; - - PowerConsumerViewHolder(View view, OnPowerConsumerSelectedListener listener) { - super(view); - mListener = listener; - view.setOnClickListener(this); - titleView = view.findViewById(android.R.id.title); - detailsView = view.findViewById(R.id.details); - iconView = view.findViewById(android.R.id.icon); - packagesView = view.findViewById(R.id.packages); - powerView = view.findViewById(R.id.power_mah); - powerView.setVisibility(View.VISIBLE); - } - - @Override - public void onClick(View v) { - mListener.onPowerConsumerSelected(id); - } - } -} diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java deleted file mode 100644 index 6d8e2c59be97..000000000000 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsviewer; - -import android.content.Context; -import android.os.BatteryConsumer; -import android.os.BatteryStats; -import android.os.BatteryUsageStats; -import android.os.Process; -import android.os.UidBatteryConsumer; -import android.os.UserHandle; - -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; - -import java.util.ArrayList; -import java.util.List; - -public class PowerStatsData { - private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; - private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; - private static final String PACKAGE_SYSTEMUI = "com.android.systemui"; - private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, - PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; - - // Temporary placeholder voltage for converting energy to charge - // TODO: remove this when b/173765509 is resolved - private static final double MOCK_NOMINAL_VOLTAGE = 3.7; - - // Unit conversion: - // mAh = uWs * (1/1000)(milli/micro) * (1/Voltage) * (1/3600)(hours/second) - private static final double UJ_2_MAH = - (1.0 / 1000) * (1.0 / MOCK_NOMINAL_VOLTAGE) * (1.0 / 3600); - - enum EntryType { - POWER, - DURATION, - } - - public static class Entry { - public String title; - public EntryType entryType; - public double value; - public double total; - } - - private final PowerConsumerInfoHelper.PowerConsumerInfo mPowerConsumerInfo; - private final List mEntries = new ArrayList<>(); - - public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper, - BatteryUsageStats batteryUsageStats, String powerConsumerId) { - List usageList = batteryStatsHelper.getUsageList(); - BatteryStats batteryStats = batteryStatsHelper.getStats(); - - double totalPowerMah = 0; - double totalSmearedPowerMah = 0; - double totalPowerExcludeSystemMah = 0; - double totalScreenPower = 0; - double totalProportionalSmearMah = 0; - double totalCpuPowerMah = 0; - double totalSystemServiceCpuPowerMah = 0; - double totalUsagePowerMah = 0; - double totalWakeLockPowerMah = 0; - double totalMobileRadioPowerMah = 0; - double totalWifiPowerMah = 0; - double totalBluetoothPowerMah = 0; - double totalGpsPowerMah = 0; - double totalCameraPowerMah = 0; - double totalFlashlightPowerMah = 0; - double totalSensorPowerMah = 0; - double totalAudioPowerMah = 0; - double totalVideoPowerMah = 0; - - long totalCpuTimeMs = 0; - long totalCpuFgTimeMs = 0; - long totalWakeLockTimeMs = 0; - long totalWifiRunningTimeMs = 0; - long totalBluetoothRunningTimeMs = 0; - long totalGpsTimeMs = 0; - long totalCameraTimeMs = 0; - long totalFlashlightTimeMs = 0; - long totalAudioTimeMs = 0; - long totalVideoTimeMs = 0; - - BatterySipper requestedBatterySipper = null; - for (BatterySipper sipper : usageList) { - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - totalScreenPower = sipper.sumPower(); - } - - if (powerConsumerId(sipper).equals(powerConsumerId)) { - requestedBatterySipper = sipper; - } - - totalPowerMah += sipper.sumPower(); - totalSmearedPowerMah += sipper.totalSmearedPowerMah; - totalProportionalSmearMah += sipper.proportionalSmearMah; - - if (!isSystemSipper(sipper)) { - totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah; - } - - totalCpuPowerMah += sipper.cpuPowerMah; - totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah; - totalUsagePowerMah += sipper.usagePowerMah; - totalWakeLockPowerMah += sipper.wakeLockPowerMah; - totalMobileRadioPowerMah += sipper.mobileRadioPowerMah; - totalWifiPowerMah += sipper.wifiPowerMah; - totalBluetoothPowerMah += sipper.bluetoothPowerMah; - totalGpsPowerMah += sipper.gpsPowerMah; - totalCameraPowerMah += sipper.cameraPowerMah; - totalFlashlightPowerMah += sipper.flashlightPowerMah; - totalSensorPowerMah += sipper.sensorPowerMah; - totalAudioPowerMah += sipper.audioPowerMah; - totalVideoPowerMah += sipper.videoPowerMah; - - totalCpuTimeMs += sipper.cpuTimeMs; - totalCpuFgTimeMs += sipper.cpuFgTimeMs; - totalWakeLockTimeMs += sipper.wakeLockTimeMs; - totalWifiRunningTimeMs += sipper.wifiRunningTimeMs; - totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs; - totalGpsTimeMs += sipper.gpsTimeMs; - totalCameraTimeMs += sipper.cameraTimeMs; - totalFlashlightTimeMs += sipper.flashlightTimeMs; - totalAudioTimeMs += sipper.audioTimeMs; - totalVideoTimeMs += sipper.videoTimeMs; - } - - BatteryConsumer requestedBatteryConsumer = null; - - for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { - if (powerConsumerId(consumer).equals(powerConsumerId)) { - requestedBatteryConsumer = consumer; - break; - } - } - - if (requestedBatterySipper == null) { - mPowerConsumerInfo = null; - return; - } - - long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); - - mPowerConsumerInfo = PowerConsumerInfoHelper.makePowerConsumerInfo( - context.getPackageManager(), requestedBatterySipper); - - addEntry("Total power", EntryType.POWER, - requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah); - maybeAddMeasuredEnergyEntry(requestedBatterySipper.drainType, batteryStats); - - addEntry("... excluding system", EntryType.POWER, - requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah); - addEntry("Screen, smeared", EntryType.POWER, - requestedBatterySipper.screenPowerMah, totalScreenPower); - if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; - final double ratio = measuredCharge / totalScreenPower; - addEntry("Screen, smeared (PowerStatsHal adjusted)", EntryType.POWER, - requestedBatterySipper.screenPowerMah * ratio, measuredCharge); - } - addEntry("Other, smeared", EntryType.POWER, - requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah); - addEntry("Excluding smeared", EntryType.POWER, - requestedBatterySipper.totalPowerMah, totalPowerMah); - if (requestedBatteryConsumer != null) { - addEntry("CPU", EntryType.POWER, - requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU), - totalCpuPowerMah); - } - addEntry("CPU (sipper)", EntryType.POWER, - requestedBatterySipper.cpuPowerMah, totalCpuPowerMah); - addEntry("System services", EntryType.POWER, - requestedBatterySipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah); - addEntry("Usage", EntryType.POWER, - requestedBatterySipper.usagePowerMah, totalUsagePowerMah); - addEntry("Wake lock", EntryType.POWER, - requestedBatterySipper.wakeLockPowerMah, totalWakeLockPowerMah); - addEntry("Mobile radio", EntryType.POWER, - requestedBatterySipper.mobileRadioPowerMah, totalMobileRadioPowerMah); - addEntry("WiFi", EntryType.POWER, - requestedBatterySipper.wifiPowerMah, totalWifiPowerMah); - addEntry("Bluetooth", EntryType.POWER, - requestedBatterySipper.bluetoothPowerMah, totalBluetoothPowerMah); - addEntry("GPS", EntryType.POWER, - requestedBatterySipper.gpsPowerMah, totalGpsPowerMah); - addEntry("Camera", EntryType.POWER, - requestedBatterySipper.cameraPowerMah, totalCameraPowerMah); - addEntry("Flashlight", EntryType.POWER, - requestedBatterySipper.flashlightPowerMah, totalFlashlightPowerMah); - addEntry("Sensors", EntryType.POWER, - requestedBatterySipper.sensorPowerMah, totalSensorPowerMah); - addEntry("Audio", EntryType.POWER, - requestedBatterySipper.audioPowerMah, totalAudioPowerMah); - addEntry("Video", EntryType.POWER, - requestedBatterySipper.videoPowerMah, totalVideoPowerMah); - - addEntry("CPU time", EntryType.DURATION, - requestedBatterySipper.cpuTimeMs, totalCpuTimeMs); - addEntry("CPU foreground time", EntryType.DURATION, - requestedBatterySipper.cpuFgTimeMs, totalCpuFgTimeMs); - addEntry("Wake lock time", EntryType.DURATION, - requestedBatterySipper.wakeLockTimeMs, totalWakeLockTimeMs); - addEntry("WiFi running time", EntryType.DURATION, - requestedBatterySipper.wifiRunningTimeMs, totalWifiRunningTimeMs); - addEntry("Bluetooth time", EntryType.DURATION, - requestedBatterySipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs); - addEntry("GPS time", EntryType.DURATION, - requestedBatterySipper.gpsTimeMs, totalGpsTimeMs); - addEntry("Camera time", EntryType.DURATION, - requestedBatterySipper.cameraTimeMs, totalCameraTimeMs); - addEntry("Flashlight time", EntryType.DURATION, - requestedBatterySipper.flashlightTimeMs, totalFlashlightTimeMs); - addEntry("Audio time", EntryType.DURATION, - requestedBatterySipper.audioTimeMs, totalAudioTimeMs); - addEntry("Video time", EntryType.DURATION, - requestedBatterySipper.videoTimeMs, totalVideoTimeMs); - } - - private boolean isSystemSipper(BatterySipper sipper) { - final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); - if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { - return true; - } else if (sipper.mPackages != null) { - for (final String packageName : sipper.mPackages) { - for (final String systemPackage : PACKAGES_SYSTEM) { - if (systemPackage.equals(packageName)) { - return true; - } - } - } - } - - return false; - } - - private void addEntry(String title, EntryType entryType, double amount, double totalAmount) { - Entry entry = new Entry(); - entry.title = title; - entry.entryType = entryType; - entry.value = amount; - entry.total = totalAmount; - mEntries.add(entry); - } - - private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType, - BatteryStats batteryStats) { - switch (drainType) { - case AMBIENT_DISPLAY: - final long totalDozeMeasuredEnergyUJ = batteryStats.getScreenDozeEnergy(); - if (totalDozeMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - final double measuredCharge = UJ_2_MAH * totalDozeMeasuredEnergyUJ; - addEntry("Measured ambient display power", EntryType.POWER, measuredCharge, - measuredCharge); - } - break; - case SCREEN: - final long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); - if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; - addEntry("Measured screen power", EntryType.POWER, measuredCharge, - measuredCharge); - } - break; - } - } - - public PowerConsumerInfoHelper.PowerConsumerInfo getPowerConsumerInfo() { - return mPowerConsumerInfo; - } - - public List getEntries() { - return mEntries; - } - - public static String powerConsumerId(BatterySipper sipper) { - return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid(); - } - - public static String powerConsumerId(BatteryConsumer consumer) { - if (consumer instanceof UidBatteryConsumer) { - return BatterySipper.DrainType.APP + "|" - + UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|" - + ((UidBatteryConsumer) consumer).getUid(); - } else { - return ""; - } - } -} diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java deleted file mode 100644 index 91533913e35d..000000000000 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2020 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.frameworks.core.powerstatsviewer; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.BatteryStats; -import android.os.BatteryStatsManager; -import android.os.BatteryUsageStats; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.activity.ComponentActivity; -import androidx.activity.result.ActivityResultLauncher; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.loader.app.LoaderManager; -import androidx.loader.app.LoaderManager.LoaderCallbacks; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.internal.os.BatteryStatsHelper; -import com.android.settingslib.utils.AsyncLoaderCompat; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -public class PowerStatsViewerActivity extends ComponentActivity { - private static final int POWER_STATS_REFRESH_RATE_MILLIS = 60 * 1000; - public static final String PREF_SELECTED_POWER_CONSUMER = "powerConsumerId"; - public static final int LOADER_BATTERY_STATS_HELPER = 0; - public static final int LOADER_BATTERY_USAGE_STATS = 1; - - private PowerStatsDataAdapter mPowerStatsDataAdapter; - private Runnable mPowerStatsRefresh = this::periodicPowerStatsRefresh; - private SharedPreferences mSharedPref; - private String mPowerConsumerId; - private TextView mTitleView; - private TextView mDetailsView; - private ImageView mIconView; - private TextView mPackagesView; - private RecyclerView mPowerStatsDataView; - private View mLoadingView; - private View mEmptyView; - private ActivityResultLauncher mStartAppPicker = registerForActivityResult( - PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected); - private BatteryStatsHelper mBatteryStatsHelper; - private BatteryUsageStats mBatteryUsageStats; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mSharedPref = getPreferences(Context.MODE_PRIVATE); - - setContentView(R.layout.power_stats_viewer_layout); - - View appCard = findViewById(R.id.app_card); - appCard.setOnClickListener((e) -> startAppPicker()); - - mTitleView = findViewById(android.R.id.title); - mDetailsView = findViewById(R.id.details); - mIconView = findViewById(android.R.id.icon); - mPackagesView = findViewById(R.id.packages); - - mPowerStatsDataView = findViewById(R.id.power_stats_data_view); - mPowerStatsDataView.setLayoutManager(new LinearLayoutManager(this)); - mPowerStatsDataAdapter = new PowerStatsDataAdapter(); - mPowerStatsDataView.setAdapter(mPowerStatsDataAdapter); - - mLoadingView = findViewById(R.id.loading_view); - mEmptyView = findViewById(R.id.empty_view); - - mPowerConsumerId = mSharedPref.getString(PREF_SELECTED_POWER_CONSUMER, null); - loadPowerStats(); - if (mPowerConsumerId == null) { - startAppPicker(); - } - } - - @Override - protected void onResume() { - super.onResume(); - periodicPowerStatsRefresh(); - } - - @Override - protected void onPause() { - super.onPause(); - getMainThreadHandler().removeCallbacks(mPowerStatsRefresh); - } - - private void startAppPicker() { - mStartAppPicker.launch(null); - } - - private void onApplicationSelected(String powerConsumerId) { - if (powerConsumerId == null) { - if (mPowerConsumerId == null) { - finish(); - } - } else { - mPowerConsumerId = powerConsumerId; - mSharedPref.edit().putString(PREF_SELECTED_POWER_CONSUMER, mPowerConsumerId).apply(); - mLoadingView.setVisibility(View.VISIBLE); - loadPowerStats(); - } - } - - private void periodicPowerStatsRefresh() { - loadPowerStats(); - getMainThreadHandler().postDelayed(mPowerStatsRefresh, POWER_STATS_REFRESH_RATE_MILLIS); - } - - private void loadPowerStats() { - LoaderManager loaderManager = LoaderManager.getInstance(this); - loaderManager.restartLoader(LOADER_BATTERY_STATS_HELPER, null, - new BatteryStatsHelperLoaderCallbacks()); - loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null, - new BatteryUsageStatsLoaderCallbacks()); - } - - private static class BatteryStatsHelperLoader extends AsyncLoaderCompat { - private final BatteryStatsHelper mBatteryStatsHelper; - private final UserManager mUserManager; - - BatteryStatsHelperLoader(Context context) { - super(context); - mUserManager = context.getSystemService(UserManager.class); - mBatteryStatsHelper = new BatteryStatsHelper(context, - false /* collectBatteryBroadcast */); - mBatteryStatsHelper.create((Bundle) null); - mBatteryStatsHelper.clearStats(); - } - - @Override - public BatteryStatsHelper loadInBackground() { - mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, - UserHandle.myUserId()); - return mBatteryStatsHelper; - } - - @Override - protected void onDiscardResult(BatteryStatsHelper result) { - } - } - - private class BatteryStatsHelperLoaderCallbacks implements LoaderCallbacks { - @NonNull - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new BatteryStatsHelperLoader(PowerStatsViewerActivity.this); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, - BatteryStatsHelper batteryStatsHelper) { - onBatteryStatsHelperLoaded(batteryStatsHelper); - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - } - } - - private static class BatteryUsageStatsLoader extends AsyncLoaderCompat { - private final BatteryStatsManager mBatteryStatsManager; - - BatteryUsageStatsLoader(Context context) { - super(context); - mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class); - } - - @Override - public BatteryUsageStats loadInBackground() { - return mBatteryStatsManager.getBatteryUsageStats(); - } - - @Override - protected void onDiscardResult(BatteryUsageStats result) { - } - } - - private class BatteryUsageStatsLoaderCallbacks implements LoaderCallbacks { - @NonNull - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new BatteryUsageStatsLoader(PowerStatsViewerActivity.this); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, - BatteryUsageStats batteryUsageStats) { - onBatteryUsageStatsLoaded(batteryUsageStats); - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - } - } - - public void onBatteryStatsHelperLoaded(BatteryStatsHelper batteryStatsHelper) { - mBatteryStatsHelper = batteryStatsHelper; - onPowerStatsDataLoaded(); - } - - private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) { - mBatteryUsageStats = batteryUsageStats; - onPowerStatsDataLoaded(); - } - - public void onPowerStatsDataLoaded() { - if (mBatteryStatsHelper == null || mBatteryUsageStats == null) { - return; - } - - PowerStatsData powerStatsData = new PowerStatsData(this, mBatteryStatsHelper, - mBatteryUsageStats, mPowerConsumerId); - - PowerConsumerInfoHelper.PowerConsumerInfo - powerConsumerInfo = powerStatsData.getPowerConsumerInfo(); - if (powerConsumerInfo == null) { - mTitleView.setText("Power consumer not found"); - mPackagesView.setVisibility(View.GONE); - } else { - mTitleView.setText(powerConsumerInfo.label); - if (powerConsumerInfo.details != null) { - mDetailsView.setText(powerConsumerInfo.details); - mDetailsView.setVisibility(View.VISIBLE); - } else { - mDetailsView.setVisibility(View.GONE); - } - mIconView.setImageDrawable( - powerConsumerInfo.iconInfo.loadIcon(getPackageManager())); - - if (powerConsumerInfo.packages != null) { - mPackagesView.setText(powerConsumerInfo.packages); - mPackagesView.setVisibility(View.VISIBLE); - } else { - mPackagesView.setVisibility(View.GONE); - } - } - - mPowerStatsDataAdapter.setEntries(powerStatsData.getEntries()); - if (powerStatsData.getEntries().isEmpty()) { - mEmptyView.setVisibility(View.VISIBLE); - mPowerStatsDataView.setVisibility(View.GONE); - } else { - mEmptyView.setVisibility(View.GONE); - mPowerStatsDataView.setVisibility(View.VISIBLE); - } - - mLoadingView.setVisibility(View.GONE); - } - - private static class PowerStatsDataAdapter extends - RecyclerView.Adapter { - public static class ViewHolder extends RecyclerView.ViewHolder { - public TextView titleTextView; - public TextView amountTextView; - public TextView percentTextView; - - ViewHolder(View itemView) { - super(itemView); - - titleTextView = itemView.findViewById(R.id.title); - amountTextView = itemView.findViewById(R.id.amount); - percentTextView = itemView.findViewById(R.id.percent); - } - } - - private List mEntries = Collections.emptyList(); - - public void setEntries(List entries) { - mEntries = entries; - notifyDataSetChanged(); - } - - @Override - public int getItemCount() { - return mEntries.size(); - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { - LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); - View itemView = layoutInflater.inflate(R.layout.power_stats_entry_layout, parent, - false); - return new ViewHolder(itemView); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { - PowerStatsData.Entry entry = mEntries.get(position); - switch (entry.entryType) { - case POWER: - viewHolder.titleTextView.setText(entry.title); - viewHolder.amountTextView.setText( - String.format(Locale.getDefault(), "%.1f mAh", entry.value)); - break; - case DURATION: - viewHolder.titleTextView.setText(entry.title); - viewHolder.amountTextView.setText( - String.format(Locale.getDefault(), "%,d ms", (long) entry.value)); - break; - } - - double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0; - viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%", - proportion)); - } - } -} -- cgit v1.2.3-59-g8ed1b