Home | History | Annotate | Download | only in os
      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.SignalStrength;
     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[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         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
     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
    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
    163     public void reset() {
    164         mTotalAppMobileActiveMs = 0;
    165     }
    166 
    167     public void reset(BatteryStats stats) {
    168         reset();
    169         mStats = stats;
    170     }
    171 }
    172