1e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski/* 2e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * Copyright (C) 2015 The Android Open Source Project 3e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * 4e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 5e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * you may not use this file except in compliance with the License. 6e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * You may obtain a copy of the License at 7e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * 8e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 9e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * 10e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * Unless required by applicable law or agreed to in writing, software 11e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 12e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * See the License for the specific language governing permissions and 14e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * limitations under the License. 15e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski */ 16e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinskipackage com.android.internal.os; 17e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 18e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinskiimport android.os.BatteryStats; 19e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinskiimport android.telephony.SignalStrength; 20e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinskiimport android.util.Log; 21e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 22e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinskipublic class MobileRadioPowerCalculator extends PowerCalculator { 23e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private static final String TAG = "MobileRadioPowerController"; 24e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private static final boolean DEBUG = BatteryStatsHelper.DEBUG; 25e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private final double mPowerRadioOn; 26e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private final double[] mPowerBins = new double[SignalStrength.NUM_SIGNAL_STRENGTH_BINS]; 27e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private final double mPowerScan; 28e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private BatteryStats mStats; 29e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private long mTotalAppMobileActiveMs = 0; 30e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 31e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski /** 32e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. 33e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski */ 34e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) { 35e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system 36e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final double MOBILE_POWER = mPowerRadioOn / 3600; 37e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 38e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, 39e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 40e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, 41e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 42e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long mobileData = mobileRx + mobileTx; 43e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 44e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long radioDataUptimeMs = 45e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; 46e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) 47e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski ? (mobileData / (double)radioDataUptimeMs) 48e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski : (((double)MOBILE_BPS) / 8 / 2048); 49e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski return (MOBILE_POWER / mobilePps) / (60*60); 50e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 51e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 52e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) { 5339b29bc2303c2bb346cba705c8903062af3ff031Hui Yu double temp = 5439b29bc2303c2bb346cba705c8903062af3ff031Hui Yu profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1); 5539b29bc2303c2bb346cba705c8903062af3ff031Hui Yu if (temp != -1) { 5639b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerRadioOn = temp; 5739b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } else { 5839b29bc2303c2bb346cba705c8903062af3ff031Hui Yu double sum = 0; 5939b29bc2303c2bb346cba705c8903062af3ff031Hui Yu sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); 6039b29bc2303c2bb346cba705c8903062af3ff031Hui Yu for (int i = 0; i < mPowerBins.length; i++) { 6139b29bc2303c2bb346cba705c8903062af3ff031Hui Yu sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); 6239b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } 6339b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerRadioOn = sum / (mPowerBins.length + 1); 64e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 6539b29bc2303c2bb346cba705c8903062af3ff031Hui Yu 6639b29bc2303c2bb346cba705c8903062af3ff031Hui Yu temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1); 6739b29bc2303c2bb346cba705c8903062af3ff031Hui Yu if (temp != -1 ) { 6839b29bc2303c2bb346cba705c8903062af3ff031Hui Yu for (int i = 0; i < mPowerBins.length; i++) { 6939b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); 7039b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } 7139b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } else { 7239b29bc2303c2bb346cba705c8903062af3ff031Hui Yu double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE); 7339b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerBins[0] = idle * 25 / 180; 7439b29bc2303c2bb346cba705c8903062af3ff031Hui Yu for (int i = 1; i < mPowerBins.length; i++) { 7539b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerBins[i] = Math.max(1, idle / 256); 7639b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } 7739b29bc2303c2bb346cba705c8903062af3ff031Hui Yu } 7839b29bc2303c2bb346cba705c8903062af3ff031Hui Yu 7939b29bc2303c2bb346cba705c8903062af3ff031Hui Yu mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0); 80e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski mStats = stats; 81e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 82e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 83e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski @Override 84e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, 85e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long rawUptimeUs, int statsType) { 86e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski // Add cost of mobile traffic. 87e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, 88e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 89e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, 90e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 91e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000; 92e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileActiveCount = u.getMobileRadioActiveCount(statsType); 93e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA, 94e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 95e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA, 96e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski statsType); 97e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 98e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (app.mobileActive > 0) { 99e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski // We are tracking when the radio is up, so can use the active time to 100e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski // determine power use. 101e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski mTotalAppMobileActiveMs += app.mobileActive; 102e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60); 103e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } else { 104e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski // We are not tracking when the radio is up, so must approximate power use 105e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski // based on the number of packets. 106e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) 107e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski * getMobilePowerPerPacket(rawRealtimeUs, statsType); 108e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 109e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (DEBUG && app.mobileRadioPowerMah != 0) { 110e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski Log.d(TAG, "UID " + u.getUid() + ": mobile packets " 111e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski + (app.mobileRxPackets + app.mobileTxPackets) 112e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski + " active time " + app.mobileActive 113e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah)); 114e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 115e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 116e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 117e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski @Override 118e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, 119e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long rawUptimeUs, int statsType) { 120e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski double power = 0; 121e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long signalTimeMs = 0; 122e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long noCoverageTimeMs = 0; 123e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski for (int i = 0; i < mPowerBins.length; i++) { 124e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType) 125e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski / 1000; 126e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000); 127e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (DEBUG && p != 0) { 128e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 129e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski + BatteryStatsHelper.makemAh(p)); 130e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 131e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski power += p; 132e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski signalTimeMs += strengthTimeMs; 133e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (i == 0) { 134e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski noCoverageTimeMs = strengthTimeMs; 135e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 136e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 137e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 138e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType) 139e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski / 1000; 140e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski final double p = (scanningTimeMs * mPowerScan) / (60*60*1000); 141e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (DEBUG && p != 0) { 142e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs 143e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski + " power=" + BatteryStatsHelper.makemAh(p)); 144e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 145e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski power += p; 146e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; 147e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs; 148e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (remainingActiveTimeMs > 0) { 149e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60); 150e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 151e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 152e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski if (power != 0) { 153d9b48d5735dc499d735d28447d5000599a1d3dfdAdam Lesinski if (signalTimeMs != 0) { 154d9b48d5735dc499d735d28447d5000599a1d3dfdAdam Lesinski app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; 155d9b48d5735dc499d735d28447d5000599a1d3dfdAdam Lesinski } 156e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileActive = remainingActiveTimeMs; 157e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType); 158e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski app.mobileRadioPowerMah = power; 159e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 160e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 161e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 162e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski @Override 163e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski public void reset() { 164e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski mTotalAppMobileActiveMs = 0; 165e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 166e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski 167e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski public void reset(BatteryStats stats) { 168e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski reset(); 169e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski mStats = stats; 170e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski } 171e08af19fcc7b13d526f3dfd24d58300947cf1146Adam Lesinski} 172