• 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.os.BatteryConsumer;
19 import android.os.BatteryStats;
20 import android.os.BatteryUsageStats;
21 import android.os.BatteryUsageStatsQuery;
22 import android.os.UidBatteryConsumer;
23 import android.telephony.CellSignalStrength;
24 import android.util.Log;
25 import android.util.SparseArray;
26 
27 public class MobileRadioPowerCalculator extends PowerCalculator {
28     private static final String TAG = "MobRadioPowerCalculator";
29     private static final boolean DEBUG = PowerCalculator.DEBUG;
30 
31     private static final int NUM_SIGNAL_STRENGTH_LEVELS =
32             CellSignalStrength.getNumSignalStrengthLevels();
33 
34     private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
35 
36     private final UsageBasedPowerEstimator mActivePowerEstimator;
37     private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
38             new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
39     private final UsageBasedPowerEstimator mScanPowerEstimator;
40 
41     private static class PowerAndDuration {
42         public long durationMs;
43         public double remainingPowerMah;
44         public long totalAppDurationMs;
45         public double totalAppPowerMah;
46         public long signalDurationMs;
47         public long noCoverageDurationMs;
48     }
49 
MobileRadioPowerCalculator(PowerProfile profile)50     public MobileRadioPowerCalculator(PowerProfile profile) {
51         // Power consumption when radio is active
52         double powerRadioActiveMa =
53                 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1);
54         if (powerRadioActiveMa == -1) {
55             double sum = 0;
56             sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
57             for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
58                 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
59             }
60             powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
61         }
62 
63         mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
64 
65         // Power consumption when radio is on, but idle
66         if (profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1) != -1) {
67             for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
68                 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(
69                         profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i));
70             }
71         } else {
72             double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE);
73 
74             // Magical calculations preserved for historical compatibility
75             mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180);
76             for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
77                 mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256));
78             }
79         }
80 
81         mScanPowerEstimator = new UsageBasedPowerEstimator(
82                 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
83     }
84 
85     @Override
isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)86     public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
87         return powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO;
88     }
89 
90     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)91     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
92             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
93 
94         PowerAndDuration total = new PowerAndDuration();
95 
96         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
97                 builder.getUidBatteryConsumerBuilders();
98         BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
99 
100         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
101             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
102             final BatteryStats.Uid uid = app.getBatteryStatsUid();
103             if (keys == UNINITIALIZED_KEYS) {
104                 if (query.isProcessStateDataNeeded()) {
105                     keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
106                 } else {
107                     keys = null;
108                 }
109             }
110 
111             calculateApp(app, uid, total, query, keys);
112         }
113 
114         final long totalConsumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
115         final int powerModel = getPowerModel(totalConsumptionUC, query);
116         calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, totalConsumptionUC);
117 
118         if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) {
119             builder.getAggregateBatteryConsumerBuilder(
120                     BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
121                     .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
122                             total.durationMs)
123                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
124                             total.remainingPowerMah + total.totalAppPowerMah, powerModel);
125 
126             builder.getAggregateBatteryConsumerBuilder(
127                     BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
128                     .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
129                             total.durationMs)
130                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
131                             total.totalAppPowerMah, powerModel);
132         }
133     }
134 
calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, PowerAndDuration total, BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys)135     private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
136             PowerAndDuration total,
137             BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys) {
138         final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
139         final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
140         final int powerModel = getPowerModel(consumptionUC, query);
141         final double powerMah = calculatePower(u, powerModel, radioActiveDurationMs, consumptionUC);
142 
143         if (!app.isVirtualUid()) {
144             total.totalAppDurationMs += radioActiveDurationMs;
145             total.totalAppPowerMah += powerMah;
146         }
147 
148         app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
149                         radioActiveDurationMs)
150                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah,
151                         powerModel);
152 
153         if (query.isProcessStateDataNeeded() && keys != null) {
154             for (BatteryConsumer.Key key: keys) {
155                 final int processState = key.processState;
156                 if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
157                     // Already populated with the total across all process states
158                     continue;
159                 }
160 
161                 final long durationInStateMs =
162                         u.getMobileRadioActiveTimeInProcessState(processState) / 1000;
163                 final long consumptionInStateUc =
164                         u.getMobileRadioMeasuredBatteryConsumptionUC(processState);
165                 final double powerInStateMah = calculatePower(u, powerModel, durationInStateMs,
166                         consumptionInStateUc);
167                 app.setConsumedPower(key, powerInStateMah, powerModel);
168             }
169         }
170     }
171 
calculateDuration(BatteryStats.Uid u, int statsType)172     private long calculateDuration(BatteryStats.Uid u, int statsType) {
173         return u.getMobileRadioActiveTime(statsType) / 1000;
174     }
175 
calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, long radioActiveDurationMs, long measuredChargeUC)176     private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
177             long radioActiveDurationMs, long measuredChargeUC) {
178         if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
179             return uCtoMah(measuredChargeUC);
180         }
181 
182         if (radioActiveDurationMs > 0) {
183             return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
184         }
185         return 0;
186     }
187 
calculateRemaining(PowerAndDuration total, @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, long rawRealtimeUs, long totalConsumptionUC)188     private void calculateRemaining(PowerAndDuration total,
189             @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats,
190             long rawRealtimeUs, long totalConsumptionUC) {
191         long signalTimeMs = 0;
192         double powerMah = 0;
193 
194         if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
195             powerMah = uCtoMah(totalConsumptionUC) - total.totalAppPowerMah;
196             if (powerMah < 0) powerMah = 0;
197         }
198 
199         for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
200             long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs,
201                     BatteryStats.STATS_SINCE_CHARGED) / 1000;
202             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
203                 final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i);
204                 if (DEBUG && p != 0) {
205                     Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
206                             + BatteryStats.formatCharge(p));
207                 }
208                 powerMah += p;
209             }
210             signalTimeMs += strengthTimeMs;
211             if (i == 0) {
212                 total.noCoverageDurationMs = strengthTimeMs;
213             }
214         }
215 
216         final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs,
217                 BatteryStats.STATS_SINCE_CHARGED) / 1000;
218         long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs,
219                 BatteryStats.STATS_SINCE_CHARGED) / 1000;
220         long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs;
221 
222         if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
223             final double p = calcScanTimePowerMah(scanningTimeMs);
224             if (DEBUG && p != 0) {
225                 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
226                         + " power=" + BatteryStats.formatCharge(p));
227             }
228             powerMah += p;
229 
230             if (remainingActiveTimeMs > 0) {
231                 powerMah += calcPowerFromRadioActiveDurationMah(remainingActiveTimeMs);
232             }
233         }
234         total.durationMs = radioActiveTimeMs;
235         total.remainingPowerMah = powerMah;
236         total.signalDurationMs = signalTimeMs;
237     }
238 
239     /**
240      * Calculates active radio power consumption (in milliamp-hours) from active radio duration.
241      */
calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs)242     public double calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs) {
243         return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
244     }
245 
246     /**
247      * Calculates idle radio power consumption (in milliamp-hours) for time spent at a cell signal
248      * strength level.
249      * see {@link CellSignalStrength#getNumSignalStrengthLevels()}
250      */
calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel)251     public double calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel) {
252         return mIdlePowerEstimators[strengthLevel].calculatePower(strengthTimeMs);
253     }
254 
255     /**
256      * Calculates radio scan power consumption (in milliamp-hours) from scan time.
257      */
calcScanTimePowerMah(long scanningTimeMs)258     public double calcScanTimePowerMah(long scanningTimeMs) {
259         return mScanPowerEstimator.calculatePower(scanningTimeMs);
260     }
261 }
262