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.BatteryStats; 19 import android.telephony.CellSignalStrength; 20 import android.util.Log; 21 22 public class MobileRadioPowerCalculator extends PowerCalculator { 23 private static final String TAG = "MobileRadioPowerController"; 24 private static final boolean DEBUG = BatteryStatsHelper.DEBUG; 25 private final double mPowerRadioOn; 26 private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()]; 27 private final double mPowerScan; 28 private BatteryStats mStats; 29 private long mTotalAppMobileActiveMs = 0; 30 31 /** 32 * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. 33 */ getMobilePowerPerPacket(long rawRealtimeUs, int statsType)34 private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) { 35 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system 36 final double MOBILE_POWER = mPowerRadioOn / 3600; 37 38 final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, 39 statsType); 40 final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, 41 statsType); 42 final long mobileData = mobileRx + mobileTx; 43 44 final long radioDataUptimeMs = 45 mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; 46 final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) 47 ? (mobileData / (double)radioDataUptimeMs) 48 : (((double)MOBILE_BPS) / 8 / 2048); 49 return (MOBILE_POWER / mobilePps) / (60*60); 50 } 51 MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats)52 public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) { 53 double temp = 54 profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1); 55 if (temp != -1) { 56 mPowerRadioOn = temp; 57 } else { 58 double sum = 0; 59 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); 60 for (int i = 0; i < mPowerBins.length; i++) { 61 sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); 62 } 63 mPowerRadioOn = sum / (mPowerBins.length + 1); 64 } 65 66 temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1); 67 if (temp != -1 ) { 68 for (int i = 0; i < mPowerBins.length; i++) { 69 mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); 70 } 71 } else { 72 double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE); 73 mPowerBins[0] = idle * 25 / 180; 74 for (int i = 1; i < mPowerBins.length; i++) { 75 mPowerBins[i] = Math.max(1, idle / 256); 76 } 77 } 78 79 mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0); 80 mStats = stats; 81 } 82 83 @Override calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType)84 public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, 85 long rawUptimeUs, int statsType) { 86 // Add cost of mobile traffic. 87 app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, 88 statsType); 89 app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, 90 statsType); 91 app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000; 92 app.mobileActiveCount = u.getMobileRadioActiveCount(statsType); 93 app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA, 94 statsType); 95 app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA, 96 statsType); 97 98 if (app.mobileActive > 0) { 99 // We are tracking when the radio is up, so can use the active time to 100 // determine power use. 101 mTotalAppMobileActiveMs += app.mobileActive; 102 app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60); 103 } else { 104 // We are not tracking when the radio is up, so must approximate power use 105 // based on the number of packets. 106 app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) 107 * getMobilePowerPerPacket(rawRealtimeUs, statsType); 108 } 109 if (DEBUG && app.mobileRadioPowerMah != 0) { 110 Log.d(TAG, "UID " + u.getUid() + ": mobile packets " 111 + (app.mobileRxPackets + app.mobileTxPackets) 112 + " active time " + app.mobileActive 113 + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah)); 114 } 115 } 116 117 @Override calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType)118 public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, 119 long rawUptimeUs, int statsType) { 120 double power = 0; 121 long signalTimeMs = 0; 122 long noCoverageTimeMs = 0; 123 for (int i = 0; i < mPowerBins.length; i++) { 124 long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType) 125 / 1000; 126 final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000); 127 if (DEBUG && p != 0) { 128 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 129 + BatteryStatsHelper.makemAh(p)); 130 } 131 power += p; 132 signalTimeMs += strengthTimeMs; 133 if (i == 0) { 134 noCoverageTimeMs = strengthTimeMs; 135 } 136 } 137 138 final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType) 139 / 1000; 140 final double p = (scanningTimeMs * mPowerScan) / (60*60*1000); 141 if (DEBUG && p != 0) { 142 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs 143 + " power=" + BatteryStatsHelper.makemAh(p)); 144 } 145 power += p; 146 long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; 147 long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs; 148 if (remainingActiveTimeMs > 0) { 149 power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60); 150 } 151 152 if (power != 0) { 153 if (signalTimeMs != 0) { 154 app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; 155 } 156 app.mobileActive = remainingActiveTimeMs; 157 app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType); 158 app.mobileRadioPowerMah = power; 159 } 160 } 161 162 @Override reset()163 public void reset() { 164 mTotalAppMobileActiveMs = 0; 165 } 166 reset(BatteryStats stats)167 public void reset(BatteryStats stats) { 168 reset(); 169 mStats = stats; 170 } 171 } 172