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