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 */ 16package com.android.internal.os; 17 18import android.os.BatteryStats; 19import android.telephony.SignalStrength; 20import android.util.Log; 21 22public 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[SignalStrength.NUM_SIGNAL_STRENGTH_BINS]; 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 */ 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 52 public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) { 53 mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE); 54 for (int i = 0; i < mPowerBins.length; i++) { 55 mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); 56 } 57 mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING); 58 mStats = stats; 59 } 60 61 @Override 62 public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, 63 long rawUptimeUs, int statsType) { 64 // Add cost of mobile traffic. 65 app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, 66 statsType); 67 app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, 68 statsType); 69 app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000; 70 app.mobileActiveCount = u.getMobileRadioActiveCount(statsType); 71 app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA, 72 statsType); 73 app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA, 74 statsType); 75 76 if (app.mobileActive > 0) { 77 // We are tracking when the radio is up, so can use the active time to 78 // determine power use. 79 mTotalAppMobileActiveMs += app.mobileActive; 80 app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60); 81 } else { 82 // We are not tracking when the radio is up, so must approximate power use 83 // based on the number of packets. 84 app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) 85 * getMobilePowerPerPacket(rawRealtimeUs, statsType); 86 } 87 if (DEBUG && app.mobileRadioPowerMah != 0) { 88 Log.d(TAG, "UID " + u.getUid() + ": mobile packets " 89 + (app.mobileRxPackets + app.mobileTxPackets) 90 + " active time " + app.mobileActive 91 + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah)); 92 } 93 } 94 95 @Override 96 public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, 97 long rawUptimeUs, int statsType) { 98 double power = 0; 99 long signalTimeMs = 0; 100 long noCoverageTimeMs = 0; 101 for (int i = 0; i < mPowerBins.length; i++) { 102 long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType) 103 / 1000; 104 final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000); 105 if (DEBUG && p != 0) { 106 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 107 + BatteryStatsHelper.makemAh(p)); 108 } 109 power += p; 110 signalTimeMs += strengthTimeMs; 111 if (i == 0) { 112 noCoverageTimeMs = strengthTimeMs; 113 } 114 } 115 116 final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType) 117 / 1000; 118 final double p = (scanningTimeMs * mPowerScan) / (60*60*1000); 119 if (DEBUG && p != 0) { 120 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs 121 + " power=" + BatteryStatsHelper.makemAh(p)); 122 } 123 power += p; 124 long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; 125 long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs; 126 if (remainingActiveTimeMs > 0) { 127 power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60); 128 } 129 130 if (power != 0) { 131 if (signalTimeMs != 0) { 132 app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; 133 } 134 app.mobileActive = remainingActiveTimeMs; 135 app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType); 136 app.mobileRadioPowerMah = power; 137 } 138 } 139 140 @Override 141 public void reset() { 142 mTotalAppMobileActiveMs = 0; 143 } 144 145 public void reset(BatteryStats stats) { 146 reset(); 147 mStats = stats; 148 } 149} 150