blob: a53d46c8ceade094a1d8bef49f623d628a680de4 [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;
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800125
Adam Lesinskie08af192015-03-25 16:42:59 -0700126 public static boolean checkWifiOnly(Context context) {
127 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
128 Context.CONNECTIVITY_SERVICE);
129 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
130 }
131
Adam Lesinski17390762015-04-10 13:17:47 -0700132 public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
133 return stats.hasWifiActivityReporting() &&
134 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
135 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
136 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
Adam Lesinskie08af192015-03-25 16:42:59 -0700137 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800138
Adam Lesinskie283d332015-04-16 12:29:25 -0700139 public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
140 PowerProfile profile) {
141 return stats.hasBluetoothActivityReporting() &&
142 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
143 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
144 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
145 }
146
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800147 public BatteryStatsHelper(Context context) {
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700148 this(context, true);
149 }
150
151 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700152 this(context, collectBatteryBroadcast, checkWifiOnly(context));
Dianne Hackbornd953c532014-08-16 18:17:38 -0700153 }
154
155 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
156 mContext = context;
157 mCollectBatteryBroadcast = collectBatteryBroadcast;
158 mWifiOnly = wifiOnly;
159 }
160
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700161 public void storeStatsHistoryInFile(String fname) {
162 synchronized (sFileXfer) {
163 File path = makeFilePath(mContext, fname);
164 sFileXfer.put(path, this.getStats());
165 FileOutputStream fout = null;
166 try {
167 fout = new FileOutputStream(path);
168 Parcel hist = Parcel.obtain();
169 getStats().writeToParcelWithoutUids(hist, 0);
170 byte[] histData = hist.marshall();
171 fout.write(histData);
172 } catch (IOException e) {
173 Log.w(TAG, "Unable to write history to file", e);
174 } finally {
175 if (fout != null) {
176 try {
177 fout.close();
178 } catch (IOException e) {
179 }
180 }
181 }
182 }
183 }
184
185 public static BatteryStats statsFromFile(Context context, String fname) {
186 synchronized (sFileXfer) {
187 File path = makeFilePath(context, fname);
188 BatteryStats stats = sFileXfer.get(path);
189 if (stats != null) {
190 return stats;
191 }
192 FileInputStream fin = null;
193 try {
194 fin = new FileInputStream(path);
195 byte[] data = readFully(fin);
196 Parcel parcel = Parcel.obtain();
197 parcel.unmarshall(data, 0, data.length);
198 parcel.setDataPosition(0);
199 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
200 } catch (IOException e) {
201 Log.w(TAG, "Unable to read history to file", e);
202 } finally {
203 if (fin != null) {
204 try {
205 fin.close();
206 } catch (IOException e) {
207 }
208 }
209 }
210 }
211 return getStats(IBatteryStats.Stub.asInterface(
212 ServiceManager.getService(BatteryStats.SERVICE_NAME)));
213 }
214
215 public static void dropFile(Context context, String fname) {
216 makeFilePath(context, fname).delete();
217 }
218
219 private static File makeFilePath(Context context, String fname) {
220 return new File(context.getFilesDir(), fname);
221 }
222
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800223 /** Clears the current stats and forces recreating for future use. */
224 public void clearStats() {
225 mStats = null;
226 }
227
228 public BatteryStats getStats() {
229 if (mStats == null) {
230 load();
231 }
232 return mStats;
233 }
234
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700235 public Intent getBatteryBroadcast() {
236 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
237 load();
238 }
239 return mBatteryBroadcast;
240 }
241
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800242 public PowerProfile getPowerProfile() {
243 return mPowerProfile;
244 }
245
246 public void create(BatteryStats stats) {
247 mPowerProfile = new PowerProfile(mContext);
248 mStats = stats;
249 }
250
251 public void create(Bundle icicle) {
252 if (icicle != null) {
253 mStats = sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700254 mBatteryBroadcast = sBatteryBroadcastXfer;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800255 }
256 mBatteryInfo = IBatteryStats.Stub.asInterface(
257 ServiceManager.getService(BatteryStats.SERVICE_NAME));
258 mPowerProfile = new PowerProfile(mContext);
259 }
260
261 public void storeState() {
262 sStatsXfer = mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700263 sBatteryBroadcastXfer = mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800264 }
265
266 public static String makemAh(double power) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700267 if (power == 0) return "0";
268 else if (power < .00001) return String.format("%.8f", power);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800269 else if (power < .0001) return String.format("%.7f", power);
270 else if (power < .001) return String.format("%.6f", power);
271 else if (power < .01) return String.format("%.5f", power);
272 else if (power < .1) return String.format("%.4f", power);
273 else if (power < 1) return String.format("%.3f", power);
274 else if (power < 10) return String.format("%.2f", power);
275 else if (power < 100) return String.format("%.1f", power);
276 else return String.format("%.0f", power);
277 }
278
279 /**
280 * Refreshes the power usage list.
281 */
282 public void refreshStats(int statsType, int asUser) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700283 SparseArray<UserHandle> users = new SparseArray<>(1);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100284 users.put(asUser, new UserHandle(asUser));
285 refreshStats(statsType, users);
286 }
287
288 /**
289 * Refreshes the power usage list.
290 */
291 public void refreshStats(int statsType, List<UserHandle> asUsers) {
292 final int n = asUsers.size();
Adam Lesinskie08af192015-03-25 16:42:59 -0700293 SparseArray<UserHandle> users = new SparseArray<>(n);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100294 for (int i = 0; i < n; ++i) {
295 UserHandle userHandle = asUsers.get(i);
296 users.put(userHandle.getIdentifier(), userHandle);
297 }
298 refreshStats(statsType, users);
299 }
300
301 /**
302 * Refreshes the power usage list.
303 */
304 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
305 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800306 SystemClock.uptimeMillis() * 1000);
307 }
308
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100309 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
310 long rawUptimeUs) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800311 // Initialize mStats if necessary.
312 getStats();
313
314 mMaxPower = 0;
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700315 mMaxRealPower = 0;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800316 mComputedPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800317 mTotalPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800318
319 mUsageList.clear();
320 mWifiSippers.clear();
321 mBluetoothSippers.clear();
322 mUserSippers.clear();
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800323 mMobilemsppList.clear();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800324
325 if (mStats == null) {
326 return;
327 }
328
Adam Lesinskie08af192015-03-25 16:42:59 -0700329 if (mCpuPowerCalculator == null) {
330 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
331 }
332 mCpuPowerCalculator.reset();
333
334 if (mWakelockPowerCalculator == null) {
335 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
336 }
337 mWakelockPowerCalculator.reset();
338
339 if (mMobileRadioPowerCalculator == null) {
340 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
341 }
342 mMobileRadioPowerCalculator.reset(mStats);
343
344 if (mWifiPowerCalculator == null) {
Adam Lesinski17390762015-04-10 13:17:47 -0700345 if (checkHasWifiPowerReporting(mStats, mPowerProfile)) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700346 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
347 } else {
348 mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
349 }
350 }
351 mWifiPowerCalculator.reset();
352
353 if (mBluetoothPowerCalculator == null) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700354 if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) {
355 mBluetoothPowerCalculator = new BluetoothPowerCalculator();
356 } else {
357 mBluetoothPowerCalculator = new BluetoothPowerCalculator();
358 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700359 }
360 mBluetoothPowerCalculator.reset();
361
362 if (mSensorPowerCalculator == null) {
363 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
364 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
365 }
366 mSensorPowerCalculator.reset();
367
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800368 mStatsType = statsType;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800369 mRawUptime = rawUptimeUs;
370 mRawRealtime = rawRealtimeUs;
371 mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);
372 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
373 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
374 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700375 mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
376 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800377
378 if (DEBUG) {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800379 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
380 + (rawUptimeUs/1000));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800381 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
382 + (mBatteryUptime/1000));
383 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
384 + (mTypeBatteryUptime/1000));
385 }
386 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
387 * mPowerProfile.getBatteryCapacity()) / 100;
388 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
389 * mPowerProfile.getBatteryCapacity()) / 100;
390
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100391 processAppUsage(asUsers);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800392
393 // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
394 for (int i=0; i<mUsageList.size(); i++) {
395 BatterySipper bs = mUsageList.get(i);
396 bs.computeMobilemspp();
397 if (bs.mobilemspp != 0) {
398 mMobilemsppList.add(bs);
399 }
400 }
Adam Lesinski33dac552015-03-09 15:24:48 -0700401
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800402 for (int i=0; i<mUserSippers.size(); i++) {
403 List<BatterySipper> user = mUserSippers.valueAt(i);
404 for (int j=0; j<user.size(); j++) {
405 BatterySipper bs = user.get(j);
406 bs.computeMobilemspp();
407 if (bs.mobilemspp != 0) {
408 mMobilemsppList.add(bs);
409 }
410 }
411 }
412 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
413 @Override
414 public int compare(BatterySipper lhs, BatterySipper rhs) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700415 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800416 }
417 });
418
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800419 processMiscUsage();
420
Adam Lesinskie08af192015-03-25 16:42:59 -0700421 Collections.sort(mUsageList);
422
423 // At this point, we've sorted the list so we are guaranteed the max values are at the top.
424 // We have only added real powers so far.
425 if (!mUsageList.isEmpty()) {
426 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
427 final int usageListCount = mUsageList.size();
428 for (int i = 0; i < usageListCount; i++) {
429 mComputedPower += mUsageList.get(i).totalPowerMah;
430 }
431 }
432
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800433 if (DEBUG) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800434 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800435 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
436 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700437
Dianne Hackborn099bc622014-01-22 13:39:16 -0800438 mTotalPower = mComputedPower;
439 if (mStats.getLowDischargeAmountSinceCharge() > 1) {
440 if (mMinDrainedPower > mComputedPower) {
441 double amount = mMinDrainedPower - mComputedPower;
442 mTotalPower = mMinDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700443 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
444
445 // Insert the BatterySipper in its sorted position.
446 int index = Collections.binarySearch(mUsageList, bs);
447 if (index < 0) {
448 index = -(index + 1);
449 }
450 mUsageList.add(index, bs);
451 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800452 } else if (mMaxDrainedPower < mComputedPower) {
453 double amount = mComputedPower - mMaxDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700454
455 // Insert the BatterySipper in its sorted position.
456 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
457 int index = Collections.binarySearch(mUsageList, bs);
458 if (index < 0) {
459 index = -(index + 1);
460 }
461 mUsageList.add(index, bs);
462 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800463 }
464 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800465 }
466
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100467 private void processAppUsage(SparseArray<UserHandle> asUsers) {
468 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800469 mStatsPeriod = mTypeBatteryRealtime;
Adam Lesinski33dac552015-03-09 15:24:48 -0700470
Adam Lesinski33dac552015-03-09 15:24:48 -0700471 final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800472 final int NU = uidStats.size();
473 for (int iu = 0; iu < NU; iu++) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700474 final Uid u = uidStats.valueAt(iu);
Adam Lesinskie08af192015-03-25 16:42:59 -0700475 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
Adam Lesinski33dac552015-03-09 15:24:48 -0700476
Adam Lesinskie08af192015-03-25 16:42:59 -0700477 mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
478 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
479 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
480 mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
481 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
482 mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
Adam Lesinski33dac552015-03-09 15:24:48 -0700483
Adam Lesinskie08af192015-03-25 16:42:59 -0700484 final double totalPower = app.sumPower();
485 if (DEBUG && totalPower != 0) {
486 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
487 makemAh(totalPower)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800488 }
489
Adam Lesinski33dac552015-03-09 15:24:48 -0700490 // Add the app to the list if it is consuming power.
Adam Lesinskie08af192015-03-25 16:42:59 -0700491 if (totalPower != 0 || u.getUid() == 0) {
492 //
493 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
494 //
495 final int uid = app.getUid();
496 final int userId = UserHandle.getUserId(uid);
497 if (uid == Process.WIFI_UID) {
498 mWifiSippers.add(app);
499 } else if (uid == Process.BLUETOOTH_UID) {
500 mBluetoothSippers.add(app);
501 } else if (!forAllUsers && asUsers.get(userId) == null
502 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
503 // We are told to just report this user's apps as one large entry.
504 List<BatterySipper> list = mUserSippers.get(userId);
505 if (list == null) {
506 list = new ArrayList<>();
507 mUserSippers.put(userId, list);
508 }
509 list.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000510 } else {
Adam Lesinskie08af192015-03-25 16:42:59 -0700511 mUsageList.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000512 }
Bart Searse9b9b732015-04-07 06:14:04 +0000513
Adam Lesinskie08af192015-03-25 16:42:59 -0700514 if (uid == 0) {
515 // The device has probably been awake for longer than the screen on
516 // time and application wake lock time would account for. Assign
517 // this remainder to the OS, if possible.
518 mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
519 mRawUptime, mStatsType);
520 app.sumPower();
521 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800522 }
523 }
524 }
525
526 private void addPhoneUsage() {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800527 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800528 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
529 * phoneOnTimeMs / (60*60*1000);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800530 if (phoneOnPower != 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700531 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800532 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800533 }
534
535 private void addScreenUsage() {
536 double power = 0;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800537 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800538 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
539 final double screenFullPower =
540 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
541 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
542 double screenBinPower = screenFullPower * (i + 0.5f)
543 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800544 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800545 / 1000;
546 double p = screenBinPower*brightnessTime;
547 if (DEBUG && p != 0) {
548 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
Dianne Hackborn099bc622014-01-22 13:39:16 -0800549 + " power=" + makemAh(p / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800550 }
551 power += p;
552 }
553 power /= (60*60*1000); // To hours
Dianne Hackborn099bc622014-01-22 13:39:16 -0800554 if (power != 0) {
555 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
556 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800557 }
558
559 private void addRadioUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700560 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
561 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
562 mStatsType);
563 radio.sumPower();
564 if (radio.totalPowerMah > 0) {
565 mUsageList.add(radio);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800566 }
567 }
568
569 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
570 for (int i=0; i<from.size(); i++) {
571 BatterySipper wbs = from.get(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700572 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700573 bs.add(wbs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800574 }
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800575 bs.computeMobilemspp();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800576 }
577
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800578 private void addIdleUsage() {
579 long idleTimeMs = (mTypeBatteryRealtime
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800580 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800581 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
582 / (60*60*1000);
583 if (DEBUG && idlePower != 0) {
584 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
585 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800586 if (idlePower != 0) {
587 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
588 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800589 }
590
Adam Lesinski33dac552015-03-09 15:24:48 -0700591 /**
592 * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
593 * then only the WiFi process gets blamed here since we normalize power calculations and
594 * assign all the power drain to apps. If energy info is not reported, we attribute the
595 * difference between total running time of WiFi for all apps and the actual running time
596 * of WiFi to the WiFi subsystem.
597 */
598 private void addWiFiUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700599 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
600 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
601 bs.sumPower();
602 if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700603 aggregateSippers(bs, mWifiSippers, "WIFI");
Adam Lesinskie08af192015-03-25 16:42:59 -0700604 mUsageList.add(bs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700605 }
606 }
607
608 /**
609 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
610 * Bluetooth Category.
611 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800612 private void addBluetoothUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700613 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
614 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
615 mStatsType);
616 if (bs.sumPower() > 0) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800617 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
618 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800619 }
620
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700621 private void addFlashlightUsage() {
622 long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000;
623 double flashlightPower = flashlightOnTimeMs
624 * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000);
625 if (flashlightPower != 0) {
626 addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower);
627 }
628 }
629
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800630 private void addUserUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700631 for (int i = 0; i < mUserSippers.size(); i++) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800632 final int userId = mUserSippers.keyAt(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700633 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800634 bs.userId = userId;
Adam Lesinskie08af192015-03-25 16:42:59 -0700635 aggregateSippers(bs, mUserSippers.valueAt(i), "User");
636 bs.sumPower();
637 mUsageList.add(bs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800638 }
639 }
640
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800641 private void processMiscUsage() {
642 addUserUsage();
643 addPhoneUsage();
644 addScreenUsage();
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700645 addFlashlightUsage();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800646 addWiFiUsage();
647 addBluetoothUsage();
648 addIdleUsage(); // Not including cellular idle power
649 // Don't compute radio usage if it's a wifi-only device
Dianne Hackbornd953c532014-08-16 18:17:38 -0700650 if (!mWifiOnly) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800651 addRadioUsage();
652 }
653 }
654
655 private BatterySipper addEntry(DrainType drainType, long time, double power) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700656 BatterySipper bs = new BatterySipper(drainType, null, 0);
657 bs.usagePowerMah = power;
658 bs.usageTimeMs = time;
659 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800660 mUsageList.add(bs);
661 return bs;
662 }
663
664 public List<BatterySipper> getUsageList() {
665 return mUsageList;
666 }
667
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800668 public List<BatterySipper> getMobilemsppList() {
669 return mMobilemsppList;
670 }
671
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800672 public long getStatsPeriod() { return mStatsPeriod; }
673
Adam Lesinskie08af192015-03-25 16:42:59 -0700674 public int getStatsType() { return mStatsType; }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800675
676 public double getMaxPower() { return mMaxPower; }
677
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700678 public double getMaxRealPower() { return mMaxRealPower; }
679
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800680 public double getTotalPower() { return mTotalPower; }
681
Dianne Hackborn099bc622014-01-22 13:39:16 -0800682 public double getComputedPower() { return mComputedPower; }
683
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800684 public double getMinDrainedPower() {
685 return mMinDrainedPower;
686 }
687
688 public double getMaxDrainedPower() {
689 return mMaxDrainedPower;
690 }
691
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700692 public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
693
694 public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
695
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700696 public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
697 return readFully(stream, stream.available());
698 }
699
700 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
701 int pos = 0;
702 byte[] data = new byte[avail];
703 while (true) {
704 int amt = stream.read(data, pos, data.length-pos);
705 //Log.i("foo", "Read " + amt + " bytes at " + pos
706 // + " of avail " + data.length);
707 if (amt <= 0) {
708 //Log.i("foo", "**** FINISHED READING: pos=" + pos
709 // + " len=" + data.length);
710 return data;
711 }
712 pos += amt;
713 avail = stream.available();
714 if (avail > data.length-pos) {
715 byte[] newData = new byte[pos+avail];
716 System.arraycopy(data, 0, newData, 0, pos);
717 data = newData;
718 }
719 }
720 }
721
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800722 private void load() {
723 if (mBatteryInfo == null) {
724 return;
725 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700726 mStats = getStats(mBatteryInfo);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700727 if (mCollectBatteryBroadcast) {
728 mBatteryBroadcast = mContext.registerReceiver(null,
729 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
730 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800731 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700732
733 private static BatteryStatsImpl getStats(IBatteryStats service) {
734 try {
735 ParcelFileDescriptor pfd = service.getStatisticsStream();
736 if (pfd != null) {
737 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
738 try {
739 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
740 Parcel parcel = Parcel.obtain();
741 parcel.unmarshall(data, 0, data.length);
742 parcel.setDataPosition(0);
743 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
744 .createFromParcel(parcel);
745 stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
746 return stats;
747 } catch (IOException e) {
748 Log.w(TAG, "Unable to read statistics stream", e);
749 }
750 }
751 } catch (RemoteException e) {
752 Log.w(TAG, "RemoteException:", e);
753 }
Dianne Hackbornd7c92892014-08-27 16:44:24 -0700754 return new BatteryStatsImpl();
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700755 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800756}