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         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