blob: 6a85afbdb322b23249d62cdffe0daf7b8887941f [file] [log] [blame]
Dianne Hackborna7c837f2014-01-15 16:20:44 -08001/*
2 * Copyright (C) 2009 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
17package com.android.internal.os;
18
Dianne Hackborna7c837f2014-01-15 16:20:44 -080019import android.content.Context;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070020import android.content.Intent;
21import android.content.IntentFilter;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080022import android.hardware.SensorManager;
23import android.net.ConnectivityManager;
24import android.os.BatteryStats;
25import android.os.BatteryStats.Uid;
26import android.os.Bundle;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070027import android.os.MemoryFile;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080028import android.os.Parcel;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070029import android.os.ParcelFileDescriptor;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080030import android.os.Process;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.os.SystemClock;
34import android.os.UserHandle;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070035import android.util.ArrayMap;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080036import android.util.Log;
37import android.util.SparseArray;
38
39import com.android.internal.app.IBatteryStats;
40import com.android.internal.os.BatterySipper.DrainType;
41
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070042import java.io.File;
43import java.io.FileInputStream;
44import java.io.FileOutputStream;
45import java.io.IOException;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080046import java.util.ArrayList;
47import java.util.Collections;
Dianne Hackbornd45665b2014-02-26 12:35:32 -080048import java.util.Comparator;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080049import java.util.List;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080050
51/**
52 * A helper class for retrieving the power usage information for all applications and services.
53 *
54 * The caller must initialize this class as soon as activity object is ready to use (for example, in
55 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
56 */
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070057public final class BatteryStatsHelper {
Adam Lesinskie08af192015-03-25 16:42:59 -070058 static final boolean DEBUG = false;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080059
60 private static final String TAG = BatteryStatsHelper.class.getSimpleName();
61
62 private static BatteryStats sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070063 private static Intent sBatteryBroadcastXfer;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070064 private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
Dianne Hackborna7c837f2014-01-15 16:20:44 -080065
66 final private Context mContext;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070067 final private boolean mCollectBatteryBroadcast;
Dianne Hackbornd953c532014-08-16 18:17:38 -070068 final private boolean mWifiOnly;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080069
70 private IBatteryStats mBatteryInfo;
71 private BatteryStats mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070072 private Intent mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080073 private PowerProfile mPowerProfile;
74
Adam Lesinskie08af192015-03-25 16:42:59 -070075 /**
76 * List of apps using power.
77 */
78 private final List<BatterySipper> mUsageList = new ArrayList<>();
Dianne Hackborna7c837f2014-01-15 16:20:44 -080079
Adam Lesinskie08af192015-03-25 16:42:59 -070080 /**
81 * List of apps using wifi power.
82 */
83 private final List<BatterySipper> mWifiSippers = new ArrayList<>();
84
85 /**
86 * List of apps using bluetooth power.
87 */
88 private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
89
90 private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
91
92 private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
Dianne Hackbornd45665b2014-02-26 12:35:32 -080093
Dianne Hackborna7c837f2014-01-15 16:20:44 -080094 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080095
Dianne Hackborn97ae5382014-03-05 16:43:25 -080096 long mRawRealtime;
97 long mRawUptime;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080098 long mBatteryRealtime;
99 long mBatteryUptime;
100 long mTypeBatteryRealtime;
101 long mTypeBatteryUptime;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700102 long mBatteryTimeRemaining;
103 long mChargeTimeRemaining;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800104
105 private long mStatsPeriod = 0;
Adam Lesinskie08af192015-03-25 16:42:59 -0700106
107 // The largest entry by power.
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800108 private double mMaxPower = 1;
Adam Lesinskie08af192015-03-25 16:42:59 -0700109
110 // The largest real entry by power (not undercounted or overcounted).
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700111 private double mMaxRealPower = 1;
Adam Lesinskie08af192015-03-25 16:42:59 -0700112
113 // Total computed power.
Dianne Hackborn099bc622014-01-22 13:39:16 -0800114 private double mComputedPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800115 private double mTotalPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800116 private double mMinDrainedPower;
117 private double mMaxDrainedPower;
118
Adam Lesinskie08af192015-03-25 16:42:59 -0700119 PowerCalculator mCpuPowerCalculator;
120 PowerCalculator mWakelockPowerCalculator;
121 MobileRadioPowerCalculator mMobileRadioPowerCalculator;
122 PowerCalculator mWifiPowerCalculator;
123 PowerCalculator mBluetoothPowerCalculator;
124 PowerCalculator mSensorPowerCalculator;
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700125 PowerCalculator mCameraPowerCalculator;
126 PowerCalculator mFlashlightPowerCalculator;
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800127
Adam Lesinskie08af192015-03-25 16:42:59 -0700128 public static boolean checkWifiOnly(Context context) {
129 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
130 Context.CONNECTIVITY_SERVICE);
131 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
132 }
133
Adam Lesinski17390762015-04-10 13:17:47 -0700134 public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
135 return stats.hasWifiActivityReporting() &&
136 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
137 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
138 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
Adam Lesinskie08af192015-03-25 16:42:59 -0700139 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800140
Adam Lesinskie283d332015-04-16 12:29:25 -0700141 public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
142 PowerProfile profile) {
143 return stats.hasBluetoothActivityReporting() &&
144 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
145 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
146 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
147 }
148
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800149 public BatteryStatsHelper(Context context) {
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700150 this(context, true);
151 }
152
153 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700154 this(context, collectBatteryBroadcast, checkWifiOnly(context));
Dianne Hackbornd953c532014-08-16 18:17:38 -0700155 }
156
157 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
158 mContext = context;
159 mCollectBatteryBroadcast = collectBatteryBroadcast;
160 mWifiOnly = wifiOnly;
161 }
162
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700163 public void storeStatsHistoryInFile(String fname) {
164 synchronized (sFileXfer) {
165 File path = makeFilePath(mContext, fname);
166 sFileXfer.put(path, this.getStats());
167 FileOutputStream fout = null;
168 try {
169 fout = new FileOutputStream(path);
170 Parcel hist = Parcel.obtain();
171 getStats().writeToParcelWithoutUids(hist, 0);
172 byte[] histData = hist.marshall();
173 fout.write(histData);
174 } catch (IOException e) {
175 Log.w(TAG, "Unable to write history to file", e);
176 } finally {
177 if (fout != null) {
178 try {
179 fout.close();
180 } catch (IOException e) {
181 }
182 }
183 }
184 }
185 }
186
187 public static BatteryStats statsFromFile(Context context, String fname) {
188 synchronized (sFileXfer) {
189 File path = makeFilePath(context, fname);
190 BatteryStats stats = sFileXfer.get(path);
191 if (stats != null) {
192 return stats;
193 }
194 FileInputStream fin = null;
195 try {
196 fin = new FileInputStream(path);
197 byte[] data = readFully(fin);
198 Parcel parcel = Parcel.obtain();
199 parcel.unmarshall(data, 0, data.length);
200 parcel.setDataPosition(0);
201 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
202 } catch (IOException e) {
203 Log.w(TAG, "Unable to read history to file", e);
204 } finally {
205 if (fin != null) {
206 try {
207 fin.close();
208 } catch (IOException e) {
209 }
210 }
211 }
212 }
213 return getStats(IBatteryStats.Stub.asInterface(
214 ServiceManager.getService(BatteryStats.SERVICE_NAME)));
215 }
216
217 public static void dropFile(Context context, String fname) {
218 makeFilePath(context, fname).delete();
219 }
220
221 private static File makeFilePath(Context context, String fname) {
222 return new File(context.getFilesDir(), fname);
223 }
224
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800225 /** Clears the current stats and forces recreating for future use. */
226 public void clearStats() {
227 mStats = null;
228 }
229
230 public BatteryStats getStats() {
231 if (mStats == null) {
232 load();
233 }
234 return mStats;
235 }
236
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700237 public Intent getBatteryBroadcast() {
238 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
239 load();
240 }
241 return mBatteryBroadcast;
242 }
243
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800244 public PowerProfile getPowerProfile() {
245 return mPowerProfile;
246 }
247
248 public void create(BatteryStats stats) {
249 mPowerProfile = new PowerProfile(mContext);
250 mStats = stats;
251 }
252
253 public void create(Bundle icicle) {
254 if (icicle != null) {
255 mStats = sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700256 mBatteryBroadcast = sBatteryBroadcastXfer;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800257 }
258 mBatteryInfo = IBatteryStats.Stub.asInterface(
259 ServiceManager.getService(BatteryStats.SERVICE_NAME));
260 mPowerProfile = new PowerProfile(mContext);
261 }
262
263 public void storeState() {
264 sStatsXfer = mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700265 sBatteryBroadcastXfer = mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800266 }
267
268 public static String makemAh(double power) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700269 if (power == 0) return "0";
270 else if (power < .00001) return String.format("%.8f", power);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800271 else if (power < .0001) return String.format("%.7f", power);
272 else if (power < .001) return String.format("%.6f", power);
273 else if (power < .01) return String.format("%.5f", power);
274 else if (power < .1) return String.format("%.4f", power);
275 else if (power < 1) return String.format("%.3f", power);
276 else if (power < 10) return String.format("%.2f", power);
277 else if (power < 100) return String.format("%.1f", power);
278 else return String.format("%.0f", power);
279 }
280
281 /**
282 * Refreshes the power usage list.
283 */
284 public void refreshStats(int statsType, int asUser) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700285 SparseArray<UserHandle> users = new SparseArray<>(1);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100286 users.put(asUser, new UserHandle(asUser));
287 refreshStats(statsType, users);
288 }
289
290 /**
291 * Refreshes the power usage list.
292 */
293 public void refreshStats(int statsType, List<UserHandle> asUsers) {
294 final int n = asUsers.size();
Adam Lesinskie08af192015-03-25 16:42:59 -0700295 SparseArray<UserHandle> users = new SparseArray<>(n);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100296 for (int i = 0; i < n; ++i) {
297 UserHandle userHandle = asUsers.get(i);
298 users.put(userHandle.getIdentifier(), userHandle);
299 }
300 refreshStats(statsType, users);
301 }
302
303 /**
304 * Refreshes the power usage list.
305 */
306 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
307 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800308 SystemClock.uptimeMillis() * 1000);
309 }
310
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100311 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
312 long rawUptimeUs) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800313 // Initialize mStats if necessary.
314 getStats();
315
316 mMaxPower = 0;
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700317 mMaxRealPower = 0;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800318 mComputedPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800319 mTotalPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800320
321 mUsageList.clear();
322 mWifiSippers.clear();
323 mBluetoothSippers.clear();
324 mUserSippers.clear();
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800325 mMobilemsppList.clear();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800326
327 if (mStats == null) {
328 return;
329 }
330
Adam Lesinskie08af192015-03-25 16:42:59 -0700331 if (mCpuPowerCalculator == null) {
332 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
333 }
334 mCpuPowerCalculator.reset();
335
336 if (mWakelockPowerCalculator == null) {
337 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
338 }
339 mWakelockPowerCalculator.reset();
340
341 if (mMobileRadioPowerCalculator == null) {
342 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
343 }
344 mMobileRadioPowerCalculator.reset(mStats);
345
346 if (mWifiPowerCalculator == null) {
Adam Lesinski17390762015-04-10 13:17:47 -0700347 if (checkHasWifiPowerReporting(mStats, mPowerProfile)) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700348 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
349 } else {
350 mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
351 }
352 }
353 mWifiPowerCalculator.reset();
354
355 if (mBluetoothPowerCalculator == null) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700356 if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) {
Adam Lesinski8576cf92015-06-09 12:48:25 -0700357 mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
Adam Lesinskie283d332015-04-16 12:29:25 -0700358 } else {
Adam Lesinski8576cf92015-06-09 12:48:25 -0700359 mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
Adam Lesinskie283d332015-04-16 12:29:25 -0700360 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700361 }
362 mBluetoothPowerCalculator.reset();
363
364 if (mSensorPowerCalculator == null) {
365 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
366 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
367 }
368 mSensorPowerCalculator.reset();
369
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700370 if (mCameraPowerCalculator == null) {
371 mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile);
372 }
373 mCameraPowerCalculator.reset();
374
375 if (mFlashlightPowerCalculator == null) {
376 mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);
377 }
378 mFlashlightPowerCalculator.reset();
379
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800380 mStatsType = statsType;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800381 mRawUptime = rawUptimeUs;
382 mRawRealtime = rawRealtimeUs;
383 mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);
384 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
385 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
386 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700387 mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
388 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800389
390 if (DEBUG) {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800391 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
392 + (rawUptimeUs/1000));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800393 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
394 + (mBatteryUptime/1000));
395 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
396 + (mTypeBatteryUptime/1000));
397 }
398 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
399 * mPowerProfile.getBatteryCapacity()) / 100;
400 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
401 * mPowerProfile.getBatteryCapacity()) / 100;
402
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100403 processAppUsage(asUsers);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800404
405 // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
406 for (int i=0; i<mUsageList.size(); i++) {
407 BatterySipper bs = mUsageList.get(i);
408 bs.computeMobilemspp();
409 if (bs.mobilemspp != 0) {
410 mMobilemsppList.add(bs);
411 }
412 }
Adam Lesinski33dac552015-03-09 15:24:48 -0700413
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800414 for (int i=0; i<mUserSippers.size(); i++) {
415 List<BatterySipper> user = mUserSippers.valueAt(i);
416 for (int j=0; j<user.size(); j++) {
417 BatterySipper bs = user.get(j);
418 bs.computeMobilemspp();
419 if (bs.mobilemspp != 0) {
420 mMobilemsppList.add(bs);
421 }
422 }
423 }
424 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
425 @Override
426 public int compare(BatterySipper lhs, BatterySipper rhs) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700427 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800428 }
429 });
430
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800431 processMiscUsage();
432
Adam Lesinskie08af192015-03-25 16:42:59 -0700433 Collections.sort(mUsageList);
434
435 // At this point, we've sorted the list so we are guaranteed the max values are at the top.
436 // We have only added real powers so far.
437 if (!mUsageList.isEmpty()) {
438 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
439 final int usageListCount = mUsageList.size();
440 for (int i = 0; i < usageListCount; i++) {
441 mComputedPower += mUsageList.get(i).totalPowerMah;
442 }
443 }
444
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800445 if (DEBUG) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800446 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800447 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
448 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700449
Dianne Hackborn099bc622014-01-22 13:39:16 -0800450 mTotalPower = mComputedPower;
451 if (mStats.getLowDischargeAmountSinceCharge() > 1) {
452 if (mMinDrainedPower > mComputedPower) {
453 double amount = mMinDrainedPower - mComputedPower;
454 mTotalPower = mMinDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700455 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
456
457 // Insert the BatterySipper in its sorted position.
458 int index = Collections.binarySearch(mUsageList, bs);
459 if (index < 0) {
460 index = -(index + 1);
461 }
462 mUsageList.add(index, bs);
463 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800464 } else if (mMaxDrainedPower < mComputedPower) {
465 double amount = mComputedPower - mMaxDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700466
467 // Insert the BatterySipper in its sorted position.
468 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
469 int index = Collections.binarySearch(mUsageList, bs);
470 if (index < 0) {
471 index = -(index + 1);
472 }
473 mUsageList.add(index, bs);
474 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800475 }
476 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800477 }
478
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100479 private void processAppUsage(SparseArray<UserHandle> asUsers) {
480 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800481 mStatsPeriod = mTypeBatteryRealtime;
Adam Lesinski33dac552015-03-09 15:24:48 -0700482
Adam Lesinski33dac552015-03-09 15:24:48 -0700483 final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800484 final int NU = uidStats.size();
485 for (int iu = 0; iu < NU; iu++) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700486 final Uid u = uidStats.valueAt(iu);
Adam Lesinskie08af192015-03-25 16:42:59 -0700487 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
Adam Lesinski33dac552015-03-09 15:24:48 -0700488
Adam Lesinskie08af192015-03-25 16:42:59 -0700489 mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
490 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
491 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
492 mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
493 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
494 mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700495 mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
496 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
Adam Lesinski33dac552015-03-09 15:24:48 -0700497
Adam Lesinskie08af192015-03-25 16:42:59 -0700498 final double totalPower = app.sumPower();
499 if (DEBUG && totalPower != 0) {
500 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
501 makemAh(totalPower)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800502 }
503
Adam Lesinski33dac552015-03-09 15:24:48 -0700504 // Add the app to the list if it is consuming power.
Adam Lesinskie08af192015-03-25 16:42:59 -0700505 if (totalPower != 0 || u.getUid() == 0) {
506 //
507 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
508 //
509 final int uid = app.getUid();
510 final int userId = UserHandle.getUserId(uid);
511 if (uid == Process.WIFI_UID) {
512 mWifiSippers.add(app);
513 } else if (uid == Process.BLUETOOTH_UID) {
514 mBluetoothSippers.add(app);
515 } else if (!forAllUsers && asUsers.get(userId) == null
516 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
517 // We are told to just report this user's apps as one large entry.
518 List<BatterySipper> list = mUserSippers.get(userId);
519 if (list == null) {
520 list = new ArrayList<>();
521 mUserSippers.put(userId, list);
522 }
523 list.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000524 } else {
Adam Lesinskie08af192015-03-25 16:42:59 -0700525 mUsageList.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000526 }
Bart Searse9b9b732015-04-07 06:14:04 +0000527
Adam Lesinskie08af192015-03-25 16:42:59 -0700528 if (uid == 0) {
529 // The device has probably been awake for longer than the screen on
530 // time and application wake lock time would account for. Assign
531 // this remainder to the OS, if possible.
532 mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
533 mRawUptime, mStatsType);
534 app.sumPower();
535 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800536 }
537 }
538 }
539
540 private void addPhoneUsage() {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800541 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800542 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
543 * phoneOnTimeMs / (60*60*1000);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800544 if (phoneOnPower != 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700545 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800546 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800547 }
548
549 private void addScreenUsage() {
550 double power = 0;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800551 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800552 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
553 final double screenFullPower =
554 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
555 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
556 double screenBinPower = screenFullPower * (i + 0.5f)
557 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800558 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800559 / 1000;
560 double p = screenBinPower*brightnessTime;
561 if (DEBUG && p != 0) {
562 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
Dianne Hackborn099bc622014-01-22 13:39:16 -0800563 + " power=" + makemAh(p / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800564 }
565 power += p;
566 }
567 power /= (60*60*1000); // To hours
Dianne Hackborn099bc622014-01-22 13:39:16 -0800568 if (power != 0) {
569 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
570 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800571 }
572
573 private void addRadioUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700574 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
575 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
576 mStatsType);
577 radio.sumPower();
578 if (radio.totalPowerMah > 0) {
579 mUsageList.add(radio);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800580 }
581 }
582
583 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
584 for (int i=0; i<from.size(); i++) {
585 BatterySipper wbs = from.get(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700586 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700587 bs.add(wbs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800588 }
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800589 bs.computeMobilemspp();
Adam Lesinski57123002015-06-12 16:12:07 -0700590 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800591 }
592
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800593 private void addIdleUsage() {
594 long idleTimeMs = (mTypeBatteryRealtime
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800595 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800596 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
597 / (60*60*1000);
598 if (DEBUG && idlePower != 0) {
599 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
600 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800601 if (idlePower != 0) {
602 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
603 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800604 }
605
Adam Lesinski33dac552015-03-09 15:24:48 -0700606 /**
607 * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
608 * then only the WiFi process gets blamed here since we normalize power calculations and
609 * assign all the power drain to apps. If energy info is not reported, we attribute the
610 * difference between total running time of WiFi for all apps and the actual running time
611 * of WiFi to the WiFi subsystem.
612 */
613 private void addWiFiUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700614 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
615 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
Adam Lesinski57123002015-06-12 16:12:07 -0700616 aggregateSippers(bs, mWifiSippers, "WIFI");
617 if (bs.totalPowerMah > 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700618 mUsageList.add(bs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700619 }
620 }
621
622 /**
623 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
624 * Bluetooth Category.
625 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800626 private void addBluetoothUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700627 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
628 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
629 mStatsType);
Adam Lesinski57123002015-06-12 16:12:07 -0700630 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
631 if (bs.totalPowerMah > 0) {
Adam Lesinskiee36c282015-05-14 13:57:08 -0700632 mUsageList.add(bs);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800633 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800634 }
635
636 private void addUserUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700637 for (int i = 0; i < mUserSippers.size(); i++) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800638 final int userId = mUserSippers.keyAt(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700639 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800640 bs.userId = userId;
Adam Lesinskie08af192015-03-25 16:42:59 -0700641 aggregateSippers(bs, mUserSippers.valueAt(i), "User");
Adam Lesinskie08af192015-03-25 16:42:59 -0700642 mUsageList.add(bs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800643 }
644 }
645
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800646 private void processMiscUsage() {
647 addUserUsage();
648 addPhoneUsage();
649 addScreenUsage();
650 addWiFiUsage();
651 addBluetoothUsage();
652 addIdleUsage(); // Not including cellular idle power
653 // Don't compute radio usage if it's a wifi-only device
Dianne Hackbornd953c532014-08-16 18:17:38 -0700654 if (!mWifiOnly) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800655 addRadioUsage();
656 }
657 }
658
659 private BatterySipper addEntry(DrainType drainType, long time, double power) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700660 BatterySipper bs = new BatterySipper(drainType, null, 0);
661 bs.usagePowerMah = power;
662 bs.usageTimeMs = time;
663 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800664 mUsageList.add(bs);
665 return bs;
666 }
667
668 public List<BatterySipper> getUsageList() {
669 return mUsageList;
670 }
671
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800672 public List<BatterySipper> getMobilemsppList() {
673 return mMobilemsppList;
674 }
675
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800676 public long getStatsPeriod() { return mStatsPeriod; }
677
Adam Lesinskie08af192015-03-25 16:42:59 -0700678 public int getStatsType() { return mStatsType; }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800679
680 public double getMaxPower() { return mMaxPower; }
681
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700682 public double getMaxRealPower() { return mMaxRealPower; }
683
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800684 public double getTotalPower() { return mTotalPower; }
685
Dianne Hackborn099bc622014-01-22 13:39:16 -0800686 public double getComputedPower() { return mComputedPower; }
687
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800688 public double getMinDrainedPower() {
689 return mMinDrainedPower;
690 }
691
692 public double getMaxDrainedPower() {
693 return mMaxDrainedPower;
694 }
695
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700696 public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
697
698 public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
699
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700700 public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
701 return readFully(stream, stream.available());
702 }
703
704 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
705 int pos = 0;
706 byte[] data = new byte[avail];
707 while (true) {
708 int amt = stream.read(data, pos, data.length-pos);
709 //Log.i("foo", "Read " + amt + " bytes at " + pos
710 // + " of avail " + data.length);
711 if (amt <= 0) {
712 //Log.i("foo", "**** FINISHED READING: pos=" + pos
713 // + " len=" + data.length);
714 return data;
715 }
716 pos += amt;
717 avail = stream.available();
718 if (avail > data.length-pos) {
719 byte[] newData = new byte[pos+avail];
720 System.arraycopy(data, 0, newData, 0, pos);
721 data = newData;
722 }
723 }
724 }
725
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800726 private void load() {
727 if (mBatteryInfo == null) {
728 return;
729 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700730 mStats = getStats(mBatteryInfo);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700731 if (mCollectBatteryBroadcast) {
732 mBatteryBroadcast = mContext.registerReceiver(null,
733 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
734 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800735 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700736
737 private static BatteryStatsImpl getStats(IBatteryStats service) {
738 try {
739 ParcelFileDescriptor pfd = service.getStatisticsStream();
740 if (pfd != null) {
741 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
742 try {
743 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
744 Parcel parcel = Parcel.obtain();
745 parcel.unmarshall(data, 0, data.length);
746 parcel.setDataPosition(0);
747 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
748 .createFromParcel(parcel);
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700749 return stats;
750 } catch (IOException e) {
751 Log.w(TAG, "Unable to read statistics stream", e);
752 }
753 }
754 } catch (RemoteException e) {
755 Log.w(TAG, "RemoteException:", e);
756 }
Dianne Hackbornd7c92892014-08-27 16:44:24 -0700757 return new BatteryStatsImpl();
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700758 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800759}