• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.os;
17 
18 import android.annotation.Nullable;
19 import android.os.BatteryConsumer;
20 import android.os.BatteryStats;
21 import android.os.BatteryStats.ControllerActivityCounter;
22 import android.os.BatteryUsageStats;
23 import android.os.BatteryUsageStatsQuery;
24 import android.os.UidBatteryConsumer;
25 import android.util.Log;
26 import android.util.SparseArray;
27 
28 import java.util.Arrays;
29 
30 public class BluetoothPowerCalculator extends PowerCalculator {
31     private static final String TAG = "BluetoothPowerCalc";
32     private static final boolean DEBUG = PowerCalculator.DEBUG;
33 
34     private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
35 
36     private final double mIdleMa;
37     private final double mRxMa;
38     private final double mTxMa;
39     private final boolean mHasBluetoothPowerController;
40 
41     private static class PowerAndDuration {
42         // Return value of BT duration per app
43         public long durationMs;
44         // Return value of BT power per app
45         public double powerMah;
46 
47         public BatteryConsumer.Key[] keys;
48         public double[] powerPerKeyMah;
49 
50         // Aggregated BT duration across all apps
51         public long totalDurationMs;
52         // Aggregated BT power across all apps
53         public double totalPowerMah;
54     }
55 
BluetoothPowerCalculator(PowerProfile profile)56     public BluetoothPowerCalculator(PowerProfile profile) {
57         mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
58         mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
59         mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
60         mHasBluetoothPowerController = mIdleMa != 0 && mRxMa != 0 && mTxMa != 0;
61     }
62 
63     @Override
isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)64     public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
65         return powerComponent == BatteryConsumer.POWER_COMPONENT_BLUETOOTH;
66     }
67 
68     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)69     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
70             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
71         if (!batteryStats.hasBluetoothActivityReporting()) {
72             return;
73         }
74 
75         BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
76         final PowerAndDuration powerAndDuration = new PowerAndDuration();
77 
78         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
79                 builder.getUidBatteryConsumerBuilders();
80         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
81             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
82             if (keys == UNINITIALIZED_KEYS) {
83                 if (query.isProcessStateDataNeeded()) {
84                     keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
85                     powerAndDuration.keys = keys;
86                     powerAndDuration.powerPerKeyMah = new double[keys.length];
87                 } else {
88                     keys = null;
89                 }
90             }
91             calculateApp(app, powerAndDuration, query);
92         }
93 
94         final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
95         final int powerModel = getPowerModel(measuredChargeUC, query);
96         final ControllerActivityCounter activityCounter =
97                 batteryStats.getBluetoothControllerActivity();
98         calculatePowerAndDuration(null, powerModel, measuredChargeUC,
99                 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
100 
101         // Subtract what the apps used, but clamp to 0.
102         final long systemComponentDurationMs = Math.max(0,
103                 powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
104         if (DEBUG) {
105             Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
106                     + " power=" + BatteryStats.formatCharge(powerAndDuration.powerMah));
107         }
108 
109         builder.getAggregateBatteryConsumerBuilder(
110                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
111                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
112                         powerAndDuration.durationMs)
113                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
114                         Math.max(powerAndDuration.powerMah, powerAndDuration.totalPowerMah),
115                         powerModel);
116 
117         builder.getAggregateBatteryConsumerBuilder(
118                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
119                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
120                         powerAndDuration.totalDurationMs)
121                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
122                         powerAndDuration.totalPowerMah,
123                         powerModel);
124     }
125 
calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration, BatteryUsageStatsQuery query)126     private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration,
127             BatteryUsageStatsQuery query) {
128         final long measuredChargeUC =
129                 app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
130         final int powerModel = getPowerModel(measuredChargeUC, query);
131         final ControllerActivityCounter activityCounter =
132                 app.getBatteryStatsUid().getBluetoothControllerActivity();
133         calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC,
134                 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
135 
136         app.setUsageDurationMillis(
137                         BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.durationMs)
138                 .setConsumedPower(
139                         BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.powerMah,
140                         powerModel);
141 
142         if (!app.isVirtualUid()) {
143             powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
144             powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
145         }
146 
147         if (query.isProcessStateDataNeeded() && powerAndDuration.keys != null) {
148             for (int j = 0; j < powerAndDuration.keys.length; j++) {
149                 BatteryConsumer.Key key = powerAndDuration.keys[j];
150                 final int processState = key.processState;
151                 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
152                     // Already populated with the powerAndDuration across all process states
153                     continue;
154                 }
155 
156                 app.setConsumedPower(key, powerAndDuration.powerPerKeyMah[j], powerModel);
157             }
158         }
159     }
160 
161     /** Returns bluetooth power usage based on the best data available. */
calculatePowerAndDuration(@ullable BatteryStats.Uid uid, @BatteryConsumer.PowerModel int powerModel, long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower, PowerAndDuration powerAndDuration)162     private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
163             @BatteryConsumer.PowerModel int powerModel,
164             long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
165             PowerAndDuration powerAndDuration) {
166         if (counter == null) {
167             powerAndDuration.durationMs = 0;
168             powerAndDuration.powerMah = 0;
169             if (powerAndDuration.powerPerKeyMah != null) {
170                 Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
171             }
172             return;
173         }
174 
175         final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
176         final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
177         final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
178         final long idleTimeMs = idleTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
179         final long rxTimeMs = rxTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
180         final long txTimeMs = txTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
181 
182         powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs;
183 
184         if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
185             powerAndDuration.powerMah = uCtoMah(measuredChargeUC);
186             if (uid != null && powerAndDuration.keys != null) {
187                 for (int i = 0; i < powerAndDuration.keys.length; i++) {
188                     BatteryConsumer.Key key = powerAndDuration.keys[i];
189                     final int processState = key.processState;
190                     if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
191                         // Already populated with the powerAndDuration across all process states
192                         continue;
193                     }
194 
195                     powerAndDuration.powerPerKeyMah[i] =
196                             uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState));
197                 }
198             }
199         } else {
200             if (!ignoreReportedPower) {
201                 final double powerMah =
202                         counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
203                                 / (double) (1000 * 60 * 60);
204                 if (powerMah != 0) {
205                     powerAndDuration.powerMah = powerMah;
206                     if (powerAndDuration.powerPerKeyMah != null) {
207                         // Leave this use case unsupported: used energy is reported
208                         // via BluetoothActivityEnergyInfo rather than PowerStats HAL.
209                         Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
210                     }
211                     return;
212                 }
213             }
214 
215             if (mHasBluetoothPowerController) {
216                 powerAndDuration.powerMah = calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
217 
218                 if (powerAndDuration.keys != null) {
219                     for (int i = 0; i < powerAndDuration.keys.length; i++) {
220                         BatteryConsumer.Key key = powerAndDuration.keys[i];
221                         final int processState = key.processState;
222                         if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
223                             // Already populated with the powerAndDuration across all process states
224                             continue;
225                         }
226 
227                         powerAndDuration.powerPerKeyMah[i] =
228                                 calculatePowerMah(
229                                         rxTimeCounter.getCountForProcessState(processState),
230                                         txTimeCounter.getCountForProcessState(processState),
231                                         idleTimeCounter.getCountForProcessState(processState));
232                     }
233                 }
234             } else {
235                 powerAndDuration.powerMah = 0;
236                 if (powerAndDuration.powerPerKeyMah != null) {
237                     Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
238                 }
239             }
240         }
241     }
242 
243     /** Returns estimated bluetooth power usage based on usage times. */
calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs)244     public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
245         return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
246                 / (1000 * 60 * 60);
247     }
248 }
249