Merge "Add a power load test"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8e38df5f4a
13
core/tests/powertests/PowerStatsLoadTests/Android.bp
Normal file
13
core/tests/powertests/PowerStatsLoadTests/Android.bp
Normal file
@@ -0,0 +1,13 @@
|
||||
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",
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.frameworks.core.powerstatsloadtests">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.BATTERY_STATS"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<instrumentation
|
||||
android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.frameworks.core.powerstatsloadtests"
|
||||
android:label="Power Stats Load Tests" />
|
||||
|
||||
<queries>
|
||||
<!-- The load test resolves http://... intents. Let it do so. -->
|
||||
<package android:name="com.android.chrome"/>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.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<Metric> mMetrics = new ArrayList<>();
|
||||
|
||||
public PowerMetrics(BatteryStatsHelper batteryStatsHelper, int uid) {
|
||||
mMinDrainedPower = batteryStatsHelper.getMinDrainedPower();
|
||||
mMaxDrainedPower = batteryStatsHelper.getMaxDrainedPower();
|
||||
|
||||
List<BatterySipper> 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<Metric> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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.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.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;
|
||||
|
||||
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 long mStartTime;
|
||||
private volatile float mInitialBatteryLevel;
|
||||
private volatile float mCurrentBatteryLevel;
|
||||
private int mIterations;
|
||||
private PowerMetrics mInitialPowerMetrics;
|
||||
private PowerMetrics mFinalPowerMetrics;
|
||||
private List<PowerMetrics.Metric> mPowerMetricsDelta;
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
disableCharger();
|
||||
try {
|
||||
prepareBatteryLevelMonitor();
|
||||
mStartTime = SystemClock.uptimeMillis();
|
||||
base.evaluate();
|
||||
captureFinalPowerStatsData();
|
||||
} finally {
|
||||
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() {
|
||||
// TODO(b/167636754): implement this method once the charger suspension API is available
|
||||
}
|
||||
|
||||
private void enableCharger() {
|
||||
// TODO(b/167636754): implement this method once the charger suspension API is available
|
||||
}
|
||||
|
||||
private PowerMetrics readBatteryStatsData() {
|
||||
mStatsHelper.clearStats();
|
||||
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
|
||||
mUserManager.getUserProfiles());
|
||||
return new PowerMetrics(mStatsHelper, mUid);
|
||||
}
|
||||
|
||||
protected void prepareBatteryLevelMonitor() {
|
||||
Intent batteryStatus = mContext.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
handleBatteryStatus(intent);
|
||||
}
|
||||
}, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
|
||||
handleBatteryStatus(batteryStatus);
|
||||
mInitialBatteryLevel = mCurrentBatteryLevel;
|
||||
}
|
||||
|
||||
protected void handleBatteryStatus(Intent intent) {
|
||||
if (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0) {
|
||||
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<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
|
||||
List<PowerMetrics.Metric> 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<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
|
||||
List<PowerMetrics.Metric> 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<PowerMetrics.Metric> 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;
|
||||
}
|
||||
}
|
||||
@@ -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.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);
|
||||
}
|
||||
}
|
||||
@@ -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.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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user