diff options
13 files changed, 990 insertions, 0 deletions
diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java new file mode 100644 index 000000000000..4a8f63370cda --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 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.powermodel; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableList; + +/** + * ActivityReport contains the summary of the activity that consumes power + * as reported by batterystats or statsd. + */ +public class ActivityReport { + private AppList<AppActivity> mApps; + + public ImmutableList<AppActivity> getAllApps() { + return mApps.getAllApps(); + } + + public ImmutableList<AppActivity> getRegularApps() { + return mApps.getRegularApps(); + } + + public List<AppActivity> findApp(String pkg) { + return mApps.findApp(pkg); + } + + public AppActivity findApp(SpecialApp specialApp) { + return mApps.findApp(specialApp); + } + + /** + * Find a component in the GLOBAL app. + * <p> + * Returns null if either the global app doesn't exist (bad data?) or the component + * doesn't exist in the global app. + */ + public ComponentActivity findGlobalComponent(Component component) { + final AppActivity global = mApps.findApp(SpecialApp.GLOBAL); + if (global == null) { + return null; + } + return global.getComponentActivity(component); + } + + public static class Builder { + private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder(); + + public Builder() { + } + + public ActivityReport build() { + final ActivityReport result = new ActivityReport(); + result.mApps = mApps.build(); + return result; + } + + public void addActivity(Component component, Collection<ComponentActivity> activities) { + for (final ComponentActivity activity: activities) { + addActivity(component, activity); + } + } + + public void addActivity(Component component, ComponentActivity activity) { + AppActivity.Builder app = mApps.get(activity.attribution); + if (app == null) { + app = new AppActivity.Builder(); + app.setAttribution(activity.attribution); + mApps.put(activity.attribution, app); + } + app.addComponentActivity(component, activity); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java new file mode 100644 index 000000000000..b87426ce0dc3 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 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.powermodel; + +import java.util.HashMap; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class AppActivity extends AppInfo { + + private ImmutableMap<Component, ComponentActivity> mComponents; + // TODO: power rails + // private ImmutableMap<Component, PowerRailActivity> mRails; + + private AppActivity() { + } + + /** + * Returns the {@link ComponentActivity} for the {@link Component} provided, + * or null if this AppActivity does not have that component. + * @more + * If there is no ComponentActivity for a particular Component, then + * there was no usage associated with that app for the app in question. + */ + public ComponentActivity getComponentActivity(Component component) { + return mComponents.get(component); + } + + public ImmutableSet<Component> getComponents() { + return mComponents.keySet(); + } + + public ImmutableMap<Component,ComponentActivity> getComponentActivities() { + return mComponents; + } + + // TODO: power rails + // public ComponentActivity getPowerRail(Component component) { + // return mComponents.get(component); + // } + // + // public Set<Component> getPowerRails() { + // return mComponents.keySet(); + // } + + public static class Builder extends AppInfo.Builder<AppActivity> { + private HashMap<Component, ComponentActivity> mComponents = new HashMap(); + // TODO power rails. + + public Builder() { + } + + public AppActivity build() { + final AppActivity result = new AppActivity(); + init(result); + result.mComponents = ImmutableMap.copyOf(mComponents); + return result; + } + + public void addComponentActivity(Component component, ComponentActivity activity) { + mComponents.put(component, activity); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java new file mode 100644 index 000000000000..208339e8e491 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 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.powermodel; + +class AppInfo { + private AttributionKey mAttribution; + + protected AppInfo() { + } + + public boolean hasPackage(String pkg) { + return mAttribution.hasPackage(pkg); + } + + public AttributionKey getAttribution() { + return mAttribution; + } + + abstract static class Builder<APP extends AppInfo> { + private AttributionKey mAttribution; + + public Builder() { + } + + public abstract APP build(); + + protected void init(AppInfo app) { + if (mAttribution == null) { + throw new RuntimeException("setAttribution(AttributionKey attribution) not called"); + } + app.mAttribution = mAttribution; + } + + public void setAttribution(AttributionKey attribution) { + mAttribution = attribution; + } + + public AttributionKey getAttribution() { + return mAttribution; + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java new file mode 100644 index 000000000000..19572fc1cf15 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/AppList.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 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.powermodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +class AppList<APP extends AppInfo> { + private ImmutableList<APP> mAllApps; + private ImmutableList<APP> mRegularApps; + private ImmutableMap<SpecialApp,APP> mSpecialApps; + + private AppList() { + } + + public ImmutableList<APP> getAllApps() { + return mAllApps; + } + + public ImmutableList<APP> getRegularApps() { + return mRegularApps; + } + + public List<APP> findApp(String pkg) { + List<APP> result = new ArrayList(); + for (APP app: mRegularApps) { + if (app.hasPackage(pkg)) { + result.add(app); + } + } + return result; + } + + public APP findApp(SpecialApp specialApp) { + return mSpecialApps.get(specialApp); + } + + public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> { + private final HashMap<AttributionKey,BUILDER> mApps = new HashMap(); + + public Builder() { + } + + public AppList<APP> build() { + final AppList<APP> result = new AppList(); + final ArrayList<APP> allApps = new ArrayList(); + final ArrayList<APP> regularApps = new ArrayList(); + final HashMap<SpecialApp,APP> specialApps = new HashMap(); + for (AppInfo.Builder<APP> app: mApps.values()) { + final AttributionKey attribution = app.getAttribution(); + final APP appActivity = app.build(); + allApps.add(appActivity); + if (attribution.isSpecialApp()) { + specialApps.put(attribution.getSpecialApp(), appActivity); + } else { + regularApps.add(appActivity); + } + } + result.mAllApps = ImmutableList.copyOf(allApps); + result.mRegularApps = ImmutableList.copyOf(regularApps); + result.mSpecialApps = ImmutableMap.copyOf(specialApps); + return result; + } + + public BUILDER get(AttributionKey attribution) { + return mApps.get(attribution); + } + + public BUILDER put(AttributionKey attribution, BUILDER app) { + return mApps.put(attribution, app); + } + } +} diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java new file mode 100644 index 000000000000..595c6612dc3b --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 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.powermodel; + +import java.io.InputStream; +import java.io.IOException; +import com.android.powermodel.component.ModemBatteryStatsReader; + +public class BatteryStatsReader { + /** + * Construct a reader. + */ + public BatteryStatsReader() { + } + + /** + * Parse a powermodel.xml file and return a PowerProfile object. + * + * @param stream An InputStream containing the batterystats output. + * + * @throws ParseException Thrown when the xml file can not be parsed. + * @throws IOException When there is a problem reading the stream. + */ + public static ActivityReport parse(InputStream stream) throws ParseException, IOException { + final Parser parser = new Parser(stream); + return parser.parse(); + } + + /** + * Implements the reading and power model logic. + */ + private static class Parser { + final InputStream mStream; + final ActivityReport mResult; + RawBatteryStats mBs; + + /** + * Constructor to capture the parameters to read. + */ + Parser(InputStream stream) { + mStream = stream; + mResult = new ActivityReport(); + } + + /** + * Read the stream, parse it, and apply the power model. + * Do not call this more than once. + */ + ActivityReport parse() throws ParseException, IOException { + mBs = RawBatteryStats.parse(mStream); + + final ActivityReport.Builder report = new ActivityReport.Builder(); + + report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs)); + + return report.build(); + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java new file mode 100644 index 000000000000..e619a96687ab --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 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.powermodel; + + +/** + * Encapsulates the work done by an app (including synthetic apps) that costs power. + */ +public class ComponentActivity { + public AttributionKey attribution; + + public ComponentActivity(AttributionKey attribution) { + this.attribution = attribution; + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java index d0c1790f4774..76c0482772f7 100644 --- a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java +++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java @@ -168,6 +168,80 @@ public class RawBatteryStats { public String lineType; } + @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class Battery extends Record { + // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get + // a parsing warning. Nobody uses anything other than STATS_SINCE_CHARGED. + @Field(index=0) + public int startCount; + + @Field(index=1) + public long whichBatteryRealtimeMs; + + @Field(index=2) + public long whichBatteryUptimeMs; + + @Field(index=3) + public long totalRealtimeMs; + + @Field(index=4) + public long totalUptimeMs; + + @Field(index=5) + public long getStartClockTimeMs; + + @Field(index=6) + public long whichBatteryScreenOffRealtimeMs; + + @Field(index=7) + public long whichBatteryScreenOffUptimeMs; + + @Field(index=8) + public long estimatedBatteryCapacityMah; + + @Field(index=9) + public long minLearnedBatteryCapacityMah; + + @Field(index=10) + public long maxLearnedBatteryCapacityMah; + + @Field(index=11) + public long screenDozeTimeMs; + } + + @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class GlobalNetwork extends Record { + @Field(index=0) + public long mobileRxTotalBytes; + + @Field(index=1) + public long mobileTxTotalBytes; + + @Field(index=2) + public long wifiRxTotalBytes; + + @Field(index=3) + public long wifiTxTotalBytes; + + @Field(index=4) + public long mobileRxTotalPackets; + + @Field(index=5) + public long mobileTxTotalPackets; + + @Field(index=6) + public long wifiRxTotalPackets; + + @Field(index=7) + public long wifiTxTotalPackets; + + @Field(index=8) + public long btRxTotalBytes; + + @Field(index=9) + public long btTxTotalBytes; + } + @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE) public static class GlobalModemController extends Record { @Field(index=0) @@ -183,6 +257,154 @@ public class RawBatteryStats { public long[] txTimeMs; } + @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class Misc extends Record { + @Field(index=0) + public long screenOnTimeMs; + + @Field(index=1) + public long phoneOnTimeMs; + + @Field(index=2) + public long fullWakeLockTimeTotalMs; + + @Field(index=3) + public long partialWakeLockTimeTotalMs; + + @Field(index=4) + public long mobileRadioActiveTimeMs; + + @Field(index=5) + public long mobileRadioActiveAdjustedTimeMs; + + @Field(index=6) + public long interactiveTimeMs; + + @Field(index=7) + public long powerSaveModeEnabledTimeMs; + + @Field(index=8) + public int connectivityChangeCount; + + @Field(index=9) + public long deepDeviceIdleModeTimeMs; + + @Field(index=10) + public long deepDeviceIdleModeCount; + + @Field(index=11) + public long deepDeviceIdlingTimeMs; + + @Field(index=12) + public long deepDeviceIdlingCount; + + @Field(index=13) + public long mobileRadioActiveCount; + + @Field(index=14) + public long mobileRadioActiveUnknownTimeMs; + + @Field(index=15) + public long lightDeviceIdleModeTimeMs; + + @Field(index=16) + public long lightDeviceIdleModeCount; + + @Field(index=17) + public long lightDeviceIdlingTimeMs; + + @Field(index=18) + public long lightDeviceIdlingCount; + + @Field(index=19) + public long lightLongestDeviceIdleModeTimeMs; + + @Field(index=20) + public long deepLongestDeviceIdleModeTimeMs; + } + + @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE) + public static class Network extends Record { + @Field(index=0) + public long mobileRxBytes; + + @Field(index=1) + public long mobileTxBytes; + + @Field(index=2) + public long wifiRxBytes; + + @Field(index=3) + public long wifiTxBytes; + + @Field(index=4) + public long mobileRxPackets; + + @Field(index=5) + public long mobileTxPackets; + + @Field(index=6) + public long wifiRxPackets; + + @Field(index=7) + public long wifiTxPackets; + + // This is microseconds, because... batterystats. + @Field(index=8) + public long mobileRadioActiveTimeUs; + + @Field(index=9) + public long mobileRadioActiveCount; + + @Field(index=10) + public long btRxBytes; + + @Field(index=11) + public long btTxBytes; + + @Field(index=12) + public long mobileWakeupCount; + + @Field(index=13) + public long wifiWakeupCount; + + @Field(index=14) + public long mobileBgRxBytes; + + @Field(index=15) + public long mobileBgTxBytes; + + @Field(index=16) + public long wifiBgRxBytes; + + @Field(index=17) + public long wifiBgTxBytes; + + @Field(index=18) + public long mobileBgRxPackets; + + @Field(index=19) + public long mobileBgTxPackets; + + @Field(index=20) + public long wifiBgRxPackets; + + @Field(index=21) + public long wifiBgTxPackets; + } + + @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class SignalStrengthTime extends Record { + @Field(index=0) + public long[] phoneSignalStrengthTimeMs; + } + + @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE) + public static class SignalScanningTime extends Record { + @Field(index=0) + public long phoneSignalScanningTimeMs; + } + @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE) public static class Uid extends Record { @Field(index=0) diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java new file mode 100644 index 000000000000..53aa3c00aa40 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.Component; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.PowerProfile; + +/** + * Encapsulates the work done by the celluar modem on behalf of an app. + */ +public class ModemAppActivity extends ComponentActivity { + /** + * Construct a new ModemAppActivity. + */ + public ModemAppActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * The number of packets received by the app. + */ + public long rxPacketCount; + + /** + * The number of packets sent by the app. + */ + public long txPacketCount; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java new file mode 100644 index 000000000000..6dbfbc24d1ef --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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.powermodel.component; + +import java.util.ArrayList; +import java.util.List; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.ComponentActivity; +import com.android.powermodel.RawBatteryStats; +import com.android.powermodel.SpecialApp; + +public class ModemBatteryStatsReader { + private ModemBatteryStatsReader() { + } + + public static List<ComponentActivity> createActivities(RawBatteryStats bs) { + final List<ComponentActivity> result = new ArrayList<ComponentActivity>(); + + // The whole system + createGlobal(result, bs); + + // The apps + createApps(result, bs); + + // The synthetic "cell" app. + createRemainder(result, bs); + + return result; + } + + private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) { + final ModemGlobalActivity global + = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL)); + + final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class); + final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class); + + // Null here just means no network activity. + if (gn != null && misc != null) { + global.rxPacketCount = gn.mobileRxTotalPackets; + global.txPacketCount = gn.mobileTxTotalPackets; + + global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs; + } + + result.add(global); + } + + private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) { + for (AttributionKey key: bs.getApps()) { + final int uid = key.getUid(); + final RawBatteryStats.Network network + = bs.getSingle(RawBatteryStats.Network.class, uid); + + // Null here just means no network activity. + if (network != null) { + final ModemAppActivity app = new ModemAppActivity(key); + + app.rxPacketCount = network.mobileRxPackets; + app.txPacketCount = network.mobileTxPackets; + + result.add(app); + } + } + } + + private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) { + final RawBatteryStats.SignalStrengthTime strength + = bs.getSingle(RawBatteryStats.SignalStrengthTime.class); + final RawBatteryStats.SignalScanningTime scanning + = bs.getSingle(RawBatteryStats.SignalScanningTime.class); + final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class); + + if (strength != null && scanning != null && misc != null) { + final ModemRemainderActivity remainder + = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER)); + + // Signal strength buckets + remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs; + + // Time spent scanning + remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs; + + // Unaccounted for active time + final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs; + long appActiveTimeMs = 0; + for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) { + appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000; + } + remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs; + + result.add(remainder); + } + } +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java new file mode 100644 index 000000000000..b9b39672f014 --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.ComponentActivity; + +/** + * Encapsulates total work done by the modem for the device. + */ +public class ModemGlobalActivity extends ComponentActivity { + /** + * Construct a new ModemGlobalActivity. + */ + public ModemGlobalActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * Returns the total number of packets received in the whole device. + */ + public long rxPacketCount; + + /** + * Returns the total number of packets sent in the whole device. + */ + public long txPacketCount; + + /** + * Returns the total time the radio was active in the whole device. + */ + public long totalActiveTimeMs; +} + diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java new file mode 100644 index 000000000000..5694de301d2d --- /dev/null +++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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.powermodel.component; + +import com.android.powermodel.ActivityReport; +import com.android.powermodel.AttributionKey; +import com.android.powermodel.Component; +import com.android.powermodel.ComponentActivity; + +/** + * Encapsulates the work done by the remaining + */ +public class ModemRemainderActivity extends ComponentActivity { + private static final double MS_PER_HR = 3600000.0; + + /** + * Construct a new ModemRemainderActivity. + */ + public ModemRemainderActivity(AttributionKey attribution) { + super(attribution); + } + + /** + * Number of milliseconds spent at each of the signal strengths. + */ + public long[] strengthTimeMs; + + /** + * Number of milliseconds spent scanning for a network. + */ + public long scanningTimeMs; + + /** + * Number of milliseconds that the radio is active for reasons other + * than an app transmitting and receiving data. + */ + public long activeTimeMs; +} + diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv new file mode 100644 index 000000000000..6e84120168ce --- /dev/null +++ b/tools/powermodel/test-resource/bs.csv @@ -0,0 +1,7 @@ +9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015 +9,0,i,uid,10139,com.google.android.gm +9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105 +9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0 +9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67 +9,0,l,sgt,3066958,0,34678,1643364,7045084 +9,0,l,sst,2443805 diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java new file mode 100644 index 000000000000..e7b2c3746c85 --- /dev/null +++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 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.powermodel; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.junit.Test; +import org.junit.Assert; + +import com.android.powermodel.component.ModemAppActivity; +import com.android.powermodel.component.ModemGlobalActivity; +import com.android.powermodel.component.ModemRemainderActivity; + +/** + * Tests {@link BatteryStatsReader}. + */ +public class BatteryStatsReaderTest { + private static InputStream loadCsvStream() { + return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv"); + } + + @Test public void testModemGlobal() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final AppActivity global = report.findApp(SpecialApp.GLOBAL); + Assert.assertNotNull(global); + + final ModemGlobalActivity modem + = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertEquals(97840, modem.rxPacketCount); + Assert.assertEquals(72941, modem.txPacketCount); + Assert.assertEquals(5113727, modem.totalActiveTimeMs); + } + + @Test public void testModemApp() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final List<AppActivity> gmailList = report.findApp("com.google.android.gm"); + Assert.assertEquals(1, gmailList.size()); + final AppActivity gmail = gmailList.get(0); + + final ModemAppActivity modem + = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertEquals(9925, modem.rxPacketCount); + Assert.assertEquals(5577, modem.txPacketCount); + } + + @Test public void testModemRemainder() throws Exception { + final ActivityReport report = BatteryStatsReader.parse(loadCsvStream()); + + final AppActivity remainder = report.findApp(SpecialApp.REMAINDER); + Assert.assertNotNull(remainder); + + final ModemRemainderActivity modem + = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM); + Assert.assertNotNull(modem); + Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 }, + modem.strengthTimeMs); + Assert.assertEquals(2443805, modem.scanningTimeMs); + Assert.assertEquals(4923676, modem.activeTimeMs); + } +} |