1 /*
2  * Copyright (C) 2016 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.server.wifi;
17 
18 import android.os.BatteryStats;
19 import android.os.RemoteException;
20 import android.os.ServiceManager;
21 import android.os.connectivity.WifiBatteryStats;
22 import android.text.format.DateUtils;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.app.IBatteryStats;
27 import com.android.server.wifi.nano.WifiMetricsProto.WifiPowerStats;
28 import com.android.server.wifi.nano.WifiMetricsProto.WifiRadioUsage;
29 
30 import java.io.PrintWriter;
31 import java.text.DecimalFormat;
32 
33 /**
34  * WifiPowerMetrics holds the wifi power metrics and converts them to WifiPowerStats proto buf.
35  * This proto buf is included in the Wifi proto buf.
36  */
37 public class WifiPowerMetrics {
38 
39     private static final String TAG = "WifiPowerMetrics";
40 
41     /* BatteryStats API */
42     private final IBatteryStats mBatteryStats;
43 
WifiPowerMetrics()44     public WifiPowerMetrics() {
45         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
46                 BatteryStats.SERVICE_NAME));
47     }
48 
49     // This constructor injects IBatteryStats and should be used for testing only.
50     @VisibleForTesting
WifiPowerMetrics(IBatteryStats batteryStats)51     public WifiPowerMetrics(IBatteryStats batteryStats) {
52         mBatteryStats = batteryStats;
53     }
54 
55     /**
56      * Build WifiPowerStats proto
57      * A snapshot of Wifi statistics in Batterystats is obtained. Due to reboots multiple correlated
58      * logs may be uploaded in a day. Resolution is on the server side. The log with longest
59      * duration is picked.
60      * @return WifiPowerStats
61      */
buildProto()62     public WifiPowerStats buildProto() {
63         WifiPowerStats m = new WifiPowerStats();
64         WifiBatteryStats stats = getStats();
65         if (stats != null) {
66             m.loggingDurationMs = stats.getLoggingDurationMs();
67             m.energyConsumedMah = stats.getEnergyConsumedMaMs()
68                 / ((double) DateUtils.HOUR_IN_MILLIS);
69             m.idleTimeMs = stats.getIdleTimeMs();
70             m.rxTimeMs = stats.getRxTimeMs();
71             m.txTimeMs = stats.getTxTimeMs();
72             m.wifiKernelActiveTimeMs = stats.getKernelActiveTimeMs();
73             m.numPacketsTx = stats.getNumPacketsTx();
74             m.numBytesTx = stats.getNumBytesTx();
75             m.numPacketsRx = stats.getNumPacketsRx();
76             m.numBytesRx = stats.getNumPacketsRx();
77             m.sleepTimeMs = stats.getSleepTimeMs();
78             m.scanTimeMs = stats.getScanTimeMs();
79             m.monitoredRailEnergyConsumedMah = stats.getMonitoredRailChargeConsumedMaMs()
80                     / ((double) DateUtils.HOUR_IN_MILLIS);
81         }
82         return m;
83     }
84 
85     /**
86      * Build WifiRadioUsage proto
87      * A snapshot of Wifi statistics in Batterystats is obtained. Due to reboots multiple correlated
88      * logs may be uploaded in a day. Server side should analyze based the ratio of collected
89      * properties over the total logging duration (ie. |scanTimeMs| / |loggingDurationMs|)
90      *
91      * This proto contains additional wifi usage data that are not directly related to power
92      * calculations.
93      * @return WifiRadioUsage
94      */
buildWifiRadioUsageProto()95     public WifiRadioUsage buildWifiRadioUsageProto() {
96         WifiRadioUsage m = new WifiRadioUsage();
97         WifiBatteryStats stats = getStats();
98         if (stats != null) {
99             m.loggingDurationMs = stats.getLoggingDurationMs();
100             m.scanTimeMs = stats.getScanTimeMs();
101         }
102         return m;
103     }
104 
105     /**
106      * Dump all WifiPowerStats to console (pw)
107      * @param pw
108      */
dump(PrintWriter pw)109     public void dump(PrintWriter pw) {
110         WifiPowerStats s = buildProto();
111         if (s!=null) {
112             pw.println("Wifi power metrics:");
113             pw.println("Logging duration (time on battery): " + s.loggingDurationMs);
114             pw.println("Energy consumed by wifi (mAh): " + s.energyConsumedMah);
115             pw.println("Amount of time wifi is in idle (ms): " + s.idleTimeMs);
116             pw.println("Amount of time wifi is in rx (ms): " + s.rxTimeMs);
117             pw.println("Amount of time wifi is in tx (ms): " + s.txTimeMs);
118             pw.println("Amount of time kernel is active because of wifi data (ms): "
119                     + s.wifiKernelActiveTimeMs);
120             pw.println("Amount of time wifi is in sleep (ms): " + s.sleepTimeMs);
121             pw.println("Amount of time wifi is scanning (ms): " + s.scanTimeMs);
122             pw.println("Number of packets sent (tx): " + s.numPacketsTx);
123             pw.println("Number of bytes sent (tx): " + s.numBytesTx);
124             pw.println("Number of packets received (rx): " + s.numPacketsRx);
125             pw.println("Number of bytes sent (rx): " + s.numBytesRx);
126             pw.println("Energy consumed across measured wifi rails (mAh): "
127                     + new DecimalFormat("#.##").format(s.monitoredRailEnergyConsumedMah));
128         }
129         WifiRadioUsage wifiRadioUsage = buildWifiRadioUsageProto();
130         pw.println("Wifi radio usage metrics:");
131         pw.println("Logging duration (time on battery): " + wifiRadioUsage.loggingDurationMs);
132         pw.println("Amount of time wifi is in scan mode while on battery (ms): "
133                 + wifiRadioUsage.scanTimeMs);
134     }
135 
136     /**
137      * Get wifi stats from batterystats
138      * @return WifiBatteryStats
139      */
getStats()140     private WifiBatteryStats getStats() {
141         try {
142             return mBatteryStats.getWifiBatteryStats();
143         } catch (RemoteException e) {
144             Log.e(TAG, "Unable to obtain Wifi power stats from BatteryStats");
145         }
146         return null;
147     }
148 }
149