blob: 59dbec6098d3c9ecba422e88c9b43bd41e742c2e [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
139 public BatteryStatsHelper(Context context) {
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700140 this(context, true);
141 }
142
143 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700144 this(context, collectBatteryBroadcast, checkWifiOnly(context));
Dianne Hackbornd953c532014-08-16 18:17:38 -0700145 }
146
147 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
148 mContext = context;
149 mCollectBatteryBroadcast = collectBatteryBroadcast;
150 mWifiOnly = wifiOnly;
151 }
152
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700153 public void storeStatsHistoryInFile(String fname) {
154 synchronized (sFileXfer) {
155 File path = makeFilePath(mContext, fname);
156 sFileXfer.put(path, this.getStats());
157 FileOutputStream fout = null;
158 try {
159 fout = new FileOutputStream(path);
160 Parcel hist = Parcel.obtain();
161 getStats().writeToParcelWithoutUids(hist, 0);
162 byte[] histData = hist.marshall();
163 fout.write(histData);
164 } catch (IOException e) {
165 Log.w(TAG, "Unable to write history to file", e);
166 } finally {
167 if (fout != null) {
168 try {
169 fout.close();
170 } catch (IOException e) {
171 }
172 }
173 }
174 }
175 }
176
177 public static BatteryStats statsFromFile(Context context, String fname) {
178 synchronized (sFileXfer) {
179 File path = makeFilePath(context, fname);
180 BatteryStats stats = sFileXfer.get(path);
181 if (stats != null) {
182 return stats;
183 }
184 FileInputStream fin = null;
185 try {
186 fin = new FileInputStream(path);
187 byte[] data = readFully(fin);
188 Parcel parcel = Parcel.obtain();
189 parcel.unmarshall(data, 0, data.length);
190 parcel.setDataPosition(0);
191 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
192 } catch (IOException e) {
193 Log.w(TAG, "Unable to read history to file", e);
194 } finally {
195 if (fin != null) {
196 try {
197 fin.close();
198 } catch (IOException e) {
199 }
200 }
201 }
202 }
203 return getStats(IBatteryStats.Stub.asInterface(
204 ServiceManager.getService(BatteryStats.SERVICE_NAME)));
205 }
206
207 public static void dropFile(Context context, String fname) {
208 makeFilePath(context, fname).delete();
209 }
210
211 private static File makeFilePath(Context context, String fname) {
212 return new File(context.getFilesDir(), fname);
213 }
214
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800215 /** Clears the current stats and forces recreating for future use. */
216 public void clearStats() {
217 mStats = null;
218 }
219
220 public BatteryStats getStats() {
221 if (mStats == null) {
222 load();
223 }
224 return mStats;
225 }
226
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700227 public Intent getBatteryBroadcast() {
228 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
229 load();
230 }
231 return mBatteryBroadcast;
232 }
233
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800234 public PowerProfile getPowerProfile() {
235 return mPowerProfile;
236 }
237
238 public void create(BatteryStats stats) {
239 mPowerProfile = new PowerProfile(mContext);
240 mStats = stats;
241 }
242
243 public void create(Bundle icicle) {
244 if (icicle != null) {
245 mStats = sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700246 mBatteryBroadcast = sBatteryBroadcastXfer;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800247 }
248 mBatteryInfo = IBatteryStats.Stub.asInterface(
249 ServiceManager.getService(BatteryStats.SERVICE_NAME));
250 mPowerProfile = new PowerProfile(mContext);
251 }
252
253 public void storeState() {
254 sStatsXfer = mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700255 sBatteryBroadcastXfer = mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800256 }
257
258 public static String makemAh(double power) {
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800259 if (power < .00001) return String.format("%.8f", power);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800260 else if (power < .0001) return String.format("%.7f", power);
261 else if (power < .001) return String.format("%.6f", power);
262 else if (power < .01) return String.format("%.5f", power);
263 else if (power < .1) return String.format("%.4f", power);
264 else if (power < 1) return String.format("%.3f", power);
265 else if (power < 10) return String.format("%.2f", power);
266 else if (power < 100) return String.format("%.1f", power);
267 else return String.format("%.0f", power);
268 }
269
270 /**
271 * Refreshes the power usage list.
272 */
273 public void refreshStats(int statsType, int asUser) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700274 SparseArray<UserHandle> users = new SparseArray<>(1);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100275 users.put(asUser, new UserHandle(asUser));
276 refreshStats(statsType, users);
277 }
278
279 /**
280 * Refreshes the power usage list.
281 */
282 public void refreshStats(int statsType, List<UserHandle> asUsers) {
283 final int n = asUsers.size();
Adam Lesinskie08af192015-03-25 16:42:59 -0700284 SparseArray<UserHandle> users = new SparseArray<>(n);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100285 for (int i = 0; i < n; ++i) {
286 UserHandle userHandle = asUsers.get(i);
287 users.put(userHandle.getIdentifier(), userHandle);
288 }
289 refreshStats(statsType, users);
290 }
291
292 /**
293 * Refreshes the power usage list.
294 */
295 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
296 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800297 SystemClock.uptimeMillis() * 1000);
298 }
299
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100300 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
301 long rawUptimeUs) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800302 // Initialize mStats if necessary.
303 getStats();
304
305 mMaxPower = 0;
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700306 mMaxRealPower = 0;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800307 mComputedPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800308 mTotalPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800309
310 mUsageList.clear();
311 mWifiSippers.clear();
312 mBluetoothSippers.clear();
313 mUserSippers.clear();
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800314 mMobilemsppList.clear();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800315
316 if (mStats == null) {
317 return;
318 }
319
Adam Lesinskie08af192015-03-25 16:42:59 -0700320 if (mCpuPowerCalculator == null) {
321 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
322 }
323 mCpuPowerCalculator.reset();
324
325 if (mWakelockPowerCalculator == null) {
326 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
327 }
328 mWakelockPowerCalculator.reset();
329
330 if (mMobileRadioPowerCalculator == null) {
331 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
332 }
333 mMobileRadioPowerCalculator.reset(mStats);
334
335 if (mWifiPowerCalculator == null) {
Adam Lesinski17390762015-04-10 13:17:47 -0700336 if (checkHasWifiPowerReporting(mStats, mPowerProfile)) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700337 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
338 } else {
339 mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
340 }
341 }
342 mWifiPowerCalculator.reset();
343
344 if (mBluetoothPowerCalculator == null) {
345 mBluetoothPowerCalculator = new BluetoothPowerCalculator();
346 }
347 mBluetoothPowerCalculator.reset();
348
349 if (mSensorPowerCalculator == null) {
350 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
351 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
352 }
353 mSensorPowerCalculator.reset();
354
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800355 mStatsType = statsType;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800356 mRawUptime = rawUptimeUs;
357 mRawRealtime = rawRealtimeUs;
358 mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);
359 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
360 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
361 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700362 mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
363 mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800364
365 if (DEBUG) {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800366 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
367 + (rawUptimeUs/1000));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800368 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
369 + (mBatteryUptime/1000));
370 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
371 + (mTypeBatteryUptime/1000));
372 }
373 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
374 * mPowerProfile.getBatteryCapacity()) / 100;
375 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
376 * mPowerProfile.getBatteryCapacity()) / 100;
377
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100378 processAppUsage(asUsers);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800379
380 // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
381 for (int i=0; i<mUsageList.size(); i++) {
382 BatterySipper bs = mUsageList.get(i);
383 bs.computeMobilemspp();
384 if (bs.mobilemspp != 0) {
385 mMobilemsppList.add(bs);
386 }
387 }
Adam Lesinski33dac552015-03-09 15:24:48 -0700388
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800389 for (int i=0; i<mUserSippers.size(); i++) {
390 List<BatterySipper> user = mUserSippers.valueAt(i);
391 for (int j=0; j<user.size(); j++) {
392 BatterySipper bs = user.get(j);
393 bs.computeMobilemspp();
394 if (bs.mobilemspp != 0) {
395 mMobilemsppList.add(bs);
396 }
397 }
398 }
399 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
400 @Override
401 public int compare(BatterySipper lhs, BatterySipper rhs) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700402 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800403 }
404 });
405
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800406 processMiscUsage();
407
Adam Lesinskie08af192015-03-25 16:42:59 -0700408 Collections.sort(mUsageList);
409
410 // At this point, we've sorted the list so we are guaranteed the max values are at the top.
411 // We have only added real powers so far.
412 if (!mUsageList.isEmpty()) {
413 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
414 final int usageListCount = mUsageList.size();
415 for (int i = 0; i < usageListCount; i++) {
416 mComputedPower += mUsageList.get(i).totalPowerMah;
417 }
418 }
419
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800420 if (DEBUG) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800421 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800422 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
423 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700424
Dianne Hackborn099bc622014-01-22 13:39:16 -0800425 mTotalPower = mComputedPower;
426 if (mStats.getLowDischargeAmountSinceCharge() > 1) {
427 if (mMinDrainedPower > mComputedPower) {
428 double amount = mMinDrainedPower - mComputedPower;
429 mTotalPower = mMinDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700430 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
431
432 // Insert the BatterySipper in its sorted position.
433 int index = Collections.binarySearch(mUsageList, bs);
434 if (index < 0) {
435 index = -(index + 1);
436 }
437 mUsageList.add(index, bs);
438 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800439 } else if (mMaxDrainedPower < mComputedPower) {
440 double amount = mComputedPower - mMaxDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700441
442 // Insert the BatterySipper in its sorted position.
443 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
444 int index = Collections.binarySearch(mUsageList, bs);
445 if (index < 0) {
446 index = -(index + 1);
447 }
448 mUsageList.add(index, bs);
449 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800450 }
451 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800452 }
453
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100454 private void processAppUsage(SparseArray<UserHandle> asUsers) {
455 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800456 mStatsPeriod = mTypeBatteryRealtime;
Adam Lesinski33dac552015-03-09 15:24:48 -0700457
Adam Lesinski33dac552015-03-09 15:24:48 -0700458 final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800459 final int NU = uidStats.size();
460 for (int iu = 0; iu < NU; iu++) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700461 final Uid u = uidStats.valueAt(iu);
Adam Lesinskie08af192015-03-25 16:42:59 -0700462 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
Adam Lesinski33dac552015-03-09 15:24:48 -0700463
Adam Lesinskie08af192015-03-25 16:42:59 -0700464 mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
465 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
466 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
467 mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
468 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
469 mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
Adam Lesinski33dac552015-03-09 15:24:48 -0700470
Adam Lesinskie08af192015-03-25 16:42:59 -0700471 final double totalPower = app.sumPower();
472 if (DEBUG && totalPower != 0) {
473 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
474 makemAh(totalPower)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800475 }
476
Adam Lesinski33dac552015-03-09 15:24:48 -0700477 // Add the app to the list if it is consuming power.
Adam Lesinskie08af192015-03-25 16:42:59 -0700478 if (totalPower != 0 || u.getUid() == 0) {
479 //
480 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
481 //
482 final int uid = app.getUid();
483 final int userId = UserHandle.getUserId(uid);
484 if (uid == Process.WIFI_UID) {
485 mWifiSippers.add(app);
486 } else if (uid == Process.BLUETOOTH_UID) {
487 mBluetoothSippers.add(app);
488 } else if (!forAllUsers && asUsers.get(userId) == null
489 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
490 // We are told to just report this user's apps as one large entry.
491 List<BatterySipper> list = mUserSippers.get(userId);
492 if (list == null) {
493 list = new ArrayList<>();
494 mUserSippers.put(userId, list);
495 }
496 list.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000497 } else {
Adam Lesinskie08af192015-03-25 16:42:59 -0700498 mUsageList.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000499 }
Bart Searse9b9b732015-04-07 06:14:04 +0000500
Adam Lesinskie08af192015-03-25 16:42:59 -0700501 if (uid == 0) {
502 // The device has probably been awake for longer than the screen on
503 // time and application wake lock time would account for. Assign
504 // this remainder to the OS, if possible.
505 mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
506 mRawUptime, mStatsType);
507 app.sumPower();
508 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800509 }
510 }
511 }
512
513 private void addPhoneUsage() {
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800514 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800515 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
516 * phoneOnTimeMs / (60*60*1000);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800517 if (phoneOnPower != 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700518 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800519 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800520 }
521
522 private void addScreenUsage() {
523 double power = 0;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800524 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800525 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
526 final double screenFullPower =
527 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
528 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
529 double screenBinPower = screenFullPower * (i + 0.5f)
530 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800531 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800532 / 1000;
533 double p = screenBinPower*brightnessTime;
534 if (DEBUG && p != 0) {
535 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
Dianne Hackborn099bc622014-01-22 13:39:16 -0800536 + " power=" + makemAh(p / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800537 }
538 power += p;
539 }
540 power /= (60*60*1000); // To hours
Dianne Hackborn099bc622014-01-22 13:39:16 -0800541 if (power != 0) {
542 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
543 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800544 }
545
546 private void addRadioUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700547 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
548 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
549 mStatsType);
550 radio.sumPower();
551 if (radio.totalPowerMah > 0) {
552 mUsageList.add(radio);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800553 }
554 }
555
556 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
557 for (int i=0; i<from.size(); i++) {
558 BatterySipper wbs = from.get(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700559 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700560 bs.add(wbs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800561 }
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800562 bs.computeMobilemspp();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800563 }
564
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800565 private void addIdleUsage() {
566 long idleTimeMs = (mTypeBatteryRealtime
Dianne Hackborn97ae5382014-03-05 16:43:25 -0800567 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800568 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
569 / (60*60*1000);
570 if (DEBUG && idlePower != 0) {
571 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
572 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800573 if (idlePower != 0) {
574 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
575 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800576 }
577
Adam Lesinski33dac552015-03-09 15:24:48 -0700578 /**
579 * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
580 * then only the WiFi process gets blamed here since we normalize power calculations and
581 * assign all the power drain to apps. If energy info is not reported, we attribute the
582 * difference between total running time of WiFi for all apps and the actual running time
583 * of WiFi to the WiFi subsystem.
584 */
585 private void addWiFiUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700586 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
587 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
588 bs.sumPower();
589 if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700590 aggregateSippers(bs, mWifiSippers, "WIFI");
Adam Lesinskie08af192015-03-25 16:42:59 -0700591 mUsageList.add(bs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700592 }
593 }
594
595 /**
596 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
597 * Bluetooth Category.
598 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800599 private void addBluetoothUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700600 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
601 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
602 mStatsType);
603 if (bs.sumPower() > 0) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800604 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
605 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800606 }
607
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700608 private void addFlashlightUsage() {
609 long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000;
610 double flashlightPower = flashlightOnTimeMs
611 * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000);
612 if (flashlightPower != 0) {
613 addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower);
614 }
615 }
616
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800617 private void addUserUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700618 for (int i = 0; i < mUserSippers.size(); i++) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800619 final int userId = mUserSippers.keyAt(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700620 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800621 bs.userId = userId;
Adam Lesinskie08af192015-03-25 16:42:59 -0700622 aggregateSippers(bs, mUserSippers.valueAt(i), "User");
623 bs.sumPower();
624 mUsageList.add(bs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800625 }
626 }
627
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800628 private void processMiscUsage() {
629 addUserUsage();
630 addPhoneUsage();
631 addScreenUsage();
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700632 addFlashlightUsage();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800633 addWiFiUsage();
634 addBluetoothUsage();
635 addIdleUsage(); // Not including cellular idle power
636 // Don't compute radio usage if it's a wifi-only device
Dianne Hackbornd953c532014-08-16 18:17:38 -0700637 if (!mWifiOnly) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800638 addRadioUsage();
639 }
640 }
641
642 private BatterySipper addEntry(DrainType drainType, long time, double power) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700643 BatterySipper bs = new BatterySipper(drainType, null, 0);
644 bs.usagePowerMah = power;
645 bs.usageTimeMs = time;
646 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800647 mUsageList.add(bs);
648 return bs;
649 }
650
651 public List<BatterySipper> getUsageList() {
652 return mUsageList;
653 }
654
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800655 public List<BatterySipper> getMobilemsppList() {
656 return mMobilemsppList;
657 }
658
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800659 public long getStatsPeriod() { return mStatsPeriod; }
660
Adam Lesinskie08af192015-03-25 16:42:59 -0700661 public int getStatsType() { return mStatsType; }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800662
663 public double getMaxPower() { return mMaxPower; }
664
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700665 public double getMaxRealPower() { return mMaxRealPower; }
666
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800667 public double getTotalPower() { return mTotalPower; }
668
Dianne Hackborn099bc622014-01-22 13:39:16 -0800669 public double getComputedPower() { return mComputedPower; }
670
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800671 public double getMinDrainedPower() {
672 return mMinDrainedPower;
673 }
674
675 public double getMaxDrainedPower() {
676 return mMaxDrainedPower;
677 }
678
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700679 public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
680
681 public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
682
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700683 public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
684 return readFully(stream, stream.available());
685 }
686
687 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
688 int pos = 0;
689 byte[] data = new byte[avail];
690 while (true) {
691 int amt = stream.read(data, pos, data.length-pos);
692 //Log.i("foo", "Read " + amt + " bytes at " + pos
693 // + " of avail " + data.length);
694 if (amt <= 0) {
695 //Log.i("foo", "**** FINISHED READING: pos=" + pos
696 // + " len=" + data.length);
697 return data;
698 }
699 pos += amt;
700 avail = stream.available();
701 if (avail > data.length-pos) {
702 byte[] newData = new byte[pos+avail];
703 System.arraycopy(data, 0, newData, 0, pos);
704 data = newData;
705 }
706 }
707 }
708
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800709 private void load() {
710 if (mBatteryInfo == null) {
711 return;
712 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700713 mStats = getStats(mBatteryInfo);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700714 if (mCollectBatteryBroadcast) {
715 mBatteryBroadcast = mContext.registerReceiver(null,
716 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
717 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800718 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700719
720 private static BatteryStatsImpl getStats(IBatteryStats service) {
721 try {
722 ParcelFileDescriptor pfd = service.getStatisticsStream();
723 if (pfd != null) {
724 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
725 try {
726 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
727 Parcel parcel = Parcel.obtain();
728 parcel.unmarshall(data, 0, data.length);
729 parcel.setDataPosition(0);
730 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
731 .createFromParcel(parcel);
732 stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
733 return stats;
734 } catch (IOException e) {
735 Log.w(TAG, "Unable to read statistics stream", e);
736 }
737 }
738 } catch (RemoteException e) {
739 Log.w(TAG, "RemoteException:", e);
740 }
Dianne Hackbornd7c92892014-08-27 16:44:24 -0700741 return new BatteryStatsImpl();
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700742 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800743}