blob: f3632f08880dd8b0900b2f8bb76bc3bf5d504172 [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;
jackqdyulei5dad8062017-06-05 16:15:54 -070022import android.content.pm.PackageManager;
23import android.content.res.Resources;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080024import android.hardware.SensorManager;
25import android.net.ConnectivityManager;
26import android.os.BatteryStats;
27import android.os.BatteryStats.Uid;
28import android.os.Bundle;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070029import android.os.MemoryFile;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080030import android.os.Parcel;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070031import android.os.ParcelFileDescriptor;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080032import android.os.Process;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.SystemClock;
36import android.os.UserHandle;
jackqdyulei5dad8062017-06-05 16:15:54 -070037import android.text.format.DateUtils;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070038import android.util.ArrayMap;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080039import android.util.Log;
40import android.util.SparseArray;
jackqdyulei5dad8062017-06-05 16:15:54 -070041import android.util.SparseLongArray;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080042
jackqdyulei5dad8062017-06-05 16:15:54 -070043import com.android.internal.annotations.VisibleForTesting;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080044import com.android.internal.app.IBatteryStats;
45import com.android.internal.os.BatterySipper.DrainType;
jackqdyulei5dad8062017-06-05 16:15:54 -070046import com.android.internal.util.ArrayUtils;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080047
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070048import java.io.File;
49import java.io.FileInputStream;
50import java.io.FileOutputStream;
51import java.io.IOException;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080052import java.util.ArrayList;
53import java.util.Collections;
Dianne Hackbornd45665b2014-02-26 12:35:32 -080054import java.util.Comparator;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080055import java.util.List;
Adam Lesinski8a83c612015-07-22 13:50:23 -070056import java.util.Locale;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080057
58/**
59 * A helper class for retrieving the power usage information for all applications and services.
60 *
61 * The caller must initialize this class as soon as activity object is ready to use (for example, in
62 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
63 */
jackqdyulei5dad8062017-06-05 16:15:54 -070064public class BatteryStatsHelper {
Adam Lesinskie08af192015-03-25 16:42:59 -070065 static final boolean DEBUG = false;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080066
67 private static final String TAG = BatteryStatsHelper.class.getSimpleName();
68
69 private static BatteryStats sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070070 private static Intent sBatteryBroadcastXfer;
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -070071 private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
Dianne Hackborna7c837f2014-01-15 16:20:44 -080072
73 final private Context mContext;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070074 final private boolean mCollectBatteryBroadcast;
Dianne Hackbornd953c532014-08-16 18:17:38 -070075 final private boolean mWifiOnly;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080076
77 private IBatteryStats mBatteryInfo;
78 private BatteryStats mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -070079 private Intent mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080080 private PowerProfile mPowerProfile;
81
jackqdyulei5dad8062017-06-05 16:15:54 -070082 private String[] mSystemPackageArray;
83 private String[] mServicepackageArray;
84 private PackageManager mPackageManager;
85
Adam Lesinskie08af192015-03-25 16:42:59 -070086 /**
87 * List of apps using power.
88 */
89 private final List<BatterySipper> mUsageList = new ArrayList<>();
Dianne Hackborna7c837f2014-01-15 16:20:44 -080090
Adam Lesinskie08af192015-03-25 16:42:59 -070091 /**
92 * List of apps using wifi power.
93 */
94 private final List<BatterySipper> mWifiSippers = new ArrayList<>();
95
96 /**
97 * List of apps using bluetooth power.
98 */
99 private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
100
101 private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
102
103 private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800104
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800105 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800106
Adam Lesinski76a267b2016-04-20 13:04:59 -0700107 long mRawRealtimeUs;
108 long mRawUptimeUs;
109 long mBatteryRealtimeUs;
110 long mBatteryUptimeUs;
111 long mTypeBatteryRealtimeUs;
112 long mTypeBatteryUptimeUs;
113 long mBatteryTimeRemainingUs;
114 long mChargeTimeRemainingUs;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800115
116 private long mStatsPeriod = 0;
Adam Lesinskie08af192015-03-25 16:42:59 -0700117
118 // The largest entry by power.
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800119 private double mMaxPower = 1;
Adam Lesinskie08af192015-03-25 16:42:59 -0700120
121 // The largest real entry by power (not undercounted or overcounted).
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700122 private double mMaxRealPower = 1;
Adam Lesinskie08af192015-03-25 16:42:59 -0700123
124 // Total computed power.
Dianne Hackborn099bc622014-01-22 13:39:16 -0800125 private double mComputedPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800126 private double mTotalPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800127 private double mMinDrainedPower;
128 private double mMaxDrainedPower;
129
Adam Lesinskie08af192015-03-25 16:42:59 -0700130 PowerCalculator mCpuPowerCalculator;
131 PowerCalculator mWakelockPowerCalculator;
132 MobileRadioPowerCalculator mMobileRadioPowerCalculator;
133 PowerCalculator mWifiPowerCalculator;
134 PowerCalculator mBluetoothPowerCalculator;
135 PowerCalculator mSensorPowerCalculator;
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700136 PowerCalculator mCameraPowerCalculator;
137 PowerCalculator mFlashlightPowerCalculator;
James Carr2dd7e5e2016-07-20 18:48:39 -0700138 PowerCalculator mMemoryPowerCalculator;
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800139
Adam Lesinskic691d3c2015-07-28 17:11:29 -0700140 boolean mHasWifiPowerReporting = false;
141 boolean mHasBluetoothPowerReporting = false;
142
Adam Lesinskie08af192015-03-25 16:42:59 -0700143 public static boolean checkWifiOnly(Context context) {
jackqdyulei5dad8062017-06-05 16:15:54 -0700144 ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
Adam Lesinskie08af192015-03-25 16:42:59 -0700145 Context.CONNECTIVITY_SERVICE);
146 return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
147 }
148
Adam Lesinski17390762015-04-10 13:17:47 -0700149 public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
150 return stats.hasWifiActivityReporting() &&
151 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
152 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
153 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
Adam Lesinskie08af192015-03-25 16:42:59 -0700154 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800155
Adam Lesinskie283d332015-04-16 12:29:25 -0700156 public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
jackqdyulei5dad8062017-06-05 16:15:54 -0700157 PowerProfile profile) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700158 return stats.hasBluetoothActivityReporting() &&
159 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
160 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
161 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
162 }
163
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800164 public BatteryStatsHelper(Context context) {
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700165 this(context, true);
166 }
167
168 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700169 this(context, collectBatteryBroadcast, checkWifiOnly(context));
Dianne Hackbornd953c532014-08-16 18:17:38 -0700170 }
171
172 public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
173 mContext = context;
174 mCollectBatteryBroadcast = collectBatteryBroadcast;
175 mWifiOnly = wifiOnly;
jackqdyulei5dad8062017-06-05 16:15:54 -0700176 mPackageManager = context.getPackageManager();
177
178 final Resources resources = context.getResources();
179 mSystemPackageArray = resources.getStringArray(
180 com.android.internal.R.array.config_batteryPackageTypeSystem);
181 mServicepackageArray = resources.getStringArray(
182 com.android.internal.R.array.config_batteryPackageTypeService);
Dianne Hackbornd953c532014-08-16 18:17:38 -0700183 }
184
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700185 public void storeStatsHistoryInFile(String fname) {
186 synchronized (sFileXfer) {
187 File path = makeFilePath(mContext, fname);
188 sFileXfer.put(path, this.getStats());
189 FileOutputStream fout = null;
190 try {
191 fout = new FileOutputStream(path);
192 Parcel hist = Parcel.obtain();
193 getStats().writeToParcelWithoutUids(hist, 0);
194 byte[] histData = hist.marshall();
195 fout.write(histData);
196 } catch (IOException e) {
197 Log.w(TAG, "Unable to write history to file", e);
198 } finally {
199 if (fout != null) {
200 try {
201 fout.close();
202 } catch (IOException e) {
203 }
204 }
205 }
206 }
207 }
208
209 public static BatteryStats statsFromFile(Context context, String fname) {
210 synchronized (sFileXfer) {
211 File path = makeFilePath(context, fname);
212 BatteryStats stats = sFileXfer.get(path);
213 if (stats != null) {
214 return stats;
215 }
216 FileInputStream fin = null;
217 try {
218 fin = new FileInputStream(path);
219 byte[] data = readFully(fin);
220 Parcel parcel = Parcel.obtain();
221 parcel.unmarshall(data, 0, data.length);
222 parcel.setDataPosition(0);
223 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
224 } catch (IOException e) {
225 Log.w(TAG, "Unable to read history to file", e);
226 } finally {
227 if (fin != null) {
228 try {
229 fin.close();
230 } catch (IOException e) {
231 }
232 }
233 }
234 }
235 return getStats(IBatteryStats.Stub.asInterface(
jackqdyulei5dad8062017-06-05 16:15:54 -0700236 ServiceManager.getService(BatteryStats.SERVICE_NAME)));
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700237 }
238
239 public static void dropFile(Context context, String fname) {
240 makeFilePath(context, fname).delete();
241 }
242
243 private static File makeFilePath(Context context, String fname) {
244 return new File(context.getFilesDir(), fname);
245 }
246
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800247 /** Clears the current stats and forces recreating for future use. */
248 public void clearStats() {
249 mStats = null;
250 }
251
252 public BatteryStats getStats() {
253 if (mStats == null) {
254 load();
255 }
256 return mStats;
257 }
258
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700259 public Intent getBatteryBroadcast() {
260 if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
261 load();
262 }
263 return mBatteryBroadcast;
264 }
265
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800266 public PowerProfile getPowerProfile() {
267 return mPowerProfile;
268 }
269
270 public void create(BatteryStats stats) {
271 mPowerProfile = new PowerProfile(mContext);
272 mStats = stats;
273 }
274
275 public void create(Bundle icicle) {
276 if (icicle != null) {
277 mStats = sStatsXfer;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700278 mBatteryBroadcast = sBatteryBroadcastXfer;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800279 }
280 mBatteryInfo = IBatteryStats.Stub.asInterface(
281 ServiceManager.getService(BatteryStats.SERVICE_NAME));
282 mPowerProfile = new PowerProfile(mContext);
283 }
284
285 public void storeState() {
286 sStatsXfer = mStats;
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700287 sBatteryBroadcastXfer = mBatteryBroadcast;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800288 }
289
290 public static String makemAh(double power) {
Adam Lesinskie283d332015-04-16 12:29:25 -0700291 if (power == 0) return "0";
Adam Lesinski8a83c612015-07-22 13:50:23 -0700292
293 final String format;
jackqdyulei5dad8062017-06-05 16:15:54 -0700294 if (power < .00001) {
295 format = "%.8f";
296 } else if (power < .0001) {
297 format = "%.7f";
298 } else if (power < .001) {
299 format = "%.6f";
300 } else if (power < .01) {
301 format = "%.5f";
302 } else if (power < .1) {
303 format = "%.4f";
304 } else if (power < 1) {
305 format = "%.3f";
306 } else if (power < 10) {
307 format = "%.2f";
308 } else if (power < 100) {
309 format = "%.1f";
310 } else {
311 format = "%.0f";
312 }
Adam Lesinski8a83c612015-07-22 13:50:23 -0700313
314 // Use English locale because this is never used in UI (only in checkin and dump).
315 return String.format(Locale.ENGLISH, format, power);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800316 }
317
318 /**
319 * Refreshes the power usage list.
320 */
321 public void refreshStats(int statsType, int asUser) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700322 SparseArray<UserHandle> users = new SparseArray<>(1);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100323 users.put(asUser, new UserHandle(asUser));
324 refreshStats(statsType, users);
325 }
326
327 /**
328 * Refreshes the power usage list.
329 */
330 public void refreshStats(int statsType, List<UserHandle> asUsers) {
331 final int n = asUsers.size();
Adam Lesinskie08af192015-03-25 16:42:59 -0700332 SparseArray<UserHandle> users = new SparseArray<>(n);
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100333 for (int i = 0; i < n; ++i) {
334 UserHandle userHandle = asUsers.get(i);
335 users.put(userHandle.getIdentifier(), userHandle);
336 }
337 refreshStats(statsType, users);
338 }
339
340 /**
341 * Refreshes the power usage list.
342 */
343 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
344 refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800345 SystemClock.uptimeMillis() * 1000);
346 }
347
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100348 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
349 long rawUptimeUs) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800350 // Initialize mStats if necessary.
351 getStats();
352
353 mMaxPower = 0;
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700354 mMaxRealPower = 0;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800355 mComputedPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800356 mTotalPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800357
358 mUsageList.clear();
359 mWifiSippers.clear();
360 mBluetoothSippers.clear();
361 mUserSippers.clear();
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800362 mMobilemsppList.clear();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800363
364 if (mStats == null) {
365 return;
366 }
367
Adam Lesinskie08af192015-03-25 16:42:59 -0700368 if (mCpuPowerCalculator == null) {
Adam Lesinski6832f392015-09-05 18:05:40 -0700369 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
Adam Lesinskie08af192015-03-25 16:42:59 -0700370 }
371 mCpuPowerCalculator.reset();
372
James Carr2dd7e5e2016-07-20 18:48:39 -0700373 if (mMemoryPowerCalculator == null) {
374 mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
375 }
376 mMemoryPowerCalculator.reset();
377
Adam Lesinskie08af192015-03-25 16:42:59 -0700378 if (mWakelockPowerCalculator == null) {
379 mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
380 }
381 mWakelockPowerCalculator.reset();
382
383 if (mMobileRadioPowerCalculator == null) {
384 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
385 }
386 mMobileRadioPowerCalculator.reset(mStats);
387
Adam Lesinskic691d3c2015-07-28 17:11:29 -0700388 // checkHasWifiPowerReporting can change if we get energy data at a later point, so
389 // always check this field.
390 final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);
391 if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {
392 mWifiPowerCalculator = hasWifiPowerReporting ?
393 new WifiPowerCalculator(mPowerProfile) :
394 new WifiPowerEstimator(mPowerProfile);
395 mHasWifiPowerReporting = hasWifiPowerReporting;
Adam Lesinskie08af192015-03-25 16:42:59 -0700396 }
397 mWifiPowerCalculator.reset();
398
Adam Lesinskic691d3c2015-07-28 17:11:29 -0700399 final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats,
jackqdyulei5dad8062017-06-05 16:15:54 -0700400 mPowerProfile);
Adam Lesinskic691d3c2015-07-28 17:11:29 -0700401 if (mBluetoothPowerCalculator == null ||
402 hasBluetoothPowerReporting != mHasBluetoothPowerReporting) {
403 mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
404 mHasBluetoothPowerReporting = hasBluetoothPowerReporting;
Adam Lesinskie08af192015-03-25 16:42:59 -0700405 }
406 mBluetoothPowerCalculator.reset();
407
408 if (mSensorPowerCalculator == null) {
409 mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
410 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
411 }
412 mSensorPowerCalculator.reset();
413
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700414 if (mCameraPowerCalculator == null) {
415 mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile);
416 }
417 mCameraPowerCalculator.reset();
418
419 if (mFlashlightPowerCalculator == null) {
420 mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);
421 }
422 mFlashlightPowerCalculator.reset();
423
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800424 mStatsType = statsType;
Adam Lesinski76a267b2016-04-20 13:04:59 -0700425 mRawUptimeUs = rawUptimeUs;
426 mRawRealtimeUs = rawRealtimeUs;
427 mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs);
428 mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs);
429 mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
430 mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
431 mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
432 mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800433
434 if (DEBUG) {
jackqdyulei5dad8062017-06-05 16:15:54 -0700435 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs / 1000) + " uptime="
436 + (rawUptimeUs / 1000));
437 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs / 1000) + " uptime="
438 + (mBatteryUptimeUs / 1000));
439 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs / 1000) + " uptime="
440 + (mTypeBatteryUptimeUs / 1000));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800441 }
442 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
443 * mPowerProfile.getBatteryCapacity()) / 100;
444 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
445 * mPowerProfile.getBatteryCapacity()) / 100;
446
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100447 processAppUsage(asUsers);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800448
449 // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
jackqdyulei5dad8062017-06-05 16:15:54 -0700450 for (int i = 0; i < mUsageList.size(); i++) {
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800451 BatterySipper bs = mUsageList.get(i);
452 bs.computeMobilemspp();
453 if (bs.mobilemspp != 0) {
454 mMobilemsppList.add(bs);
455 }
456 }
Adam Lesinski33dac552015-03-09 15:24:48 -0700457
jackqdyulei5dad8062017-06-05 16:15:54 -0700458 for (int i = 0; i < mUserSippers.size(); i++) {
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800459 List<BatterySipper> user = mUserSippers.valueAt(i);
jackqdyulei5dad8062017-06-05 16:15:54 -0700460 for (int j = 0; j < user.size(); j++) {
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800461 BatterySipper bs = user.get(j);
462 bs.computeMobilemspp();
463 if (bs.mobilemspp != 0) {
464 mMobilemsppList.add(bs);
465 }
466 }
467 }
468 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
469 @Override
470 public int compare(BatterySipper lhs, BatterySipper rhs) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700471 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800472 }
473 });
474
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800475 processMiscUsage();
476
Adam Lesinskie08af192015-03-25 16:42:59 -0700477 Collections.sort(mUsageList);
478
479 // At this point, we've sorted the list so we are guaranteed the max values are at the top.
480 // We have only added real powers so far.
481 if (!mUsageList.isEmpty()) {
482 mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
483 final int usageListCount = mUsageList.size();
484 for (int i = 0; i < usageListCount; i++) {
485 mComputedPower += mUsageList.get(i).totalPowerMah;
486 }
487 }
488
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800489 if (DEBUG) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800490 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800491 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
492 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700493
Dianne Hackborn099bc622014-01-22 13:39:16 -0800494 mTotalPower = mComputedPower;
495 if (mStats.getLowDischargeAmountSinceCharge() > 1) {
496 if (mMinDrainedPower > mComputedPower) {
497 double amount = mMinDrainedPower - mComputedPower;
498 mTotalPower = mMinDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700499 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
500
501 // Insert the BatterySipper in its sorted position.
502 int index = Collections.binarySearch(mUsageList, bs);
503 if (index < 0) {
504 index = -(index + 1);
505 }
506 mUsageList.add(index, bs);
507 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800508 } else if (mMaxDrainedPower < mComputedPower) {
509 double amount = mComputedPower - mMaxDrainedPower;
Adam Lesinskie08af192015-03-25 16:42:59 -0700510
511 // Insert the BatterySipper in its sorted position.
512 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
513 int index = Collections.binarySearch(mUsageList, bs);
514 if (index < 0) {
515 index = -(index + 1);
516 }
517 mUsageList.add(index, bs);
518 mMaxPower = Math.max(mMaxPower, amount);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800519 }
520 }
jackqdyulei5dad8062017-06-05 16:15:54 -0700521
522 // Smear it!
523 final double hiddenPowerMah = removeHiddenBatterySippers(mUsageList);
524 final double totalRemainingPower = getTotalPower() - hiddenPowerMah;
525 if (Math.abs(totalRemainingPower) > 1e-3) {
526 for (int i = 0, size = mUsageList.size(); i < size; i++) {
527 final BatterySipper sipper = mUsageList.get(i);
528 if (!sipper.shouldHide) {
529 sipper.proportionalSmearMah = hiddenPowerMah
530 * ((sipper.totalPowerMah + sipper.screenPowerMah)
531 / totalRemainingPower);
532 sipper.sumPower();
533 }
534 }
535 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800536 }
537
Zoltan Szatmary-Banc3b07a02014-07-01 17:11:07 +0100538 private void processAppUsage(SparseArray<UserHandle> asUsers) {
539 final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
Adam Lesinski76a267b2016-04-20 13:04:59 -0700540 mStatsPeriod = mTypeBatteryRealtimeUs;
Adam Lesinski33dac552015-03-09 15:24:48 -0700541
Adam Lesinski43f222f2015-06-30 12:54:41 -0700542 BatterySipper osSipper = null;
Adam Lesinski33dac552015-03-09 15:24:48 -0700543 final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800544 final int NU = uidStats.size();
545 for (int iu = 0; iu < NU; iu++) {
Adam Lesinski33dac552015-03-09 15:24:48 -0700546 final Uid u = uidStats.valueAt(iu);
Adam Lesinskie08af192015-03-25 16:42:59 -0700547 final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
Adam Lesinski33dac552015-03-09 15:24:48 -0700548
Adam Lesinski76a267b2016-04-20 13:04:59 -0700549 mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
550 mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
jackqdyulei5dad8062017-06-05 16:15:54 -0700551 mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
552 mStatsType);
Adam Lesinski76a267b2016-04-20 13:04:59 -0700553 mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
jackqdyulei5dad8062017-06-05 16:15:54 -0700554 mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
555 mStatsType);
Adam Lesinski76a267b2016-04-20 13:04:59 -0700556 mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
557 mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
jackqdyulei5dad8062017-06-05 16:15:54 -0700558 mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
559 mStatsType);
Adam Lesinski33dac552015-03-09 15:24:48 -0700560
Adam Lesinskie08af192015-03-25 16:42:59 -0700561 final double totalPower = app.sumPower();
562 if (DEBUG && totalPower != 0) {
563 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
564 makemAh(totalPower)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800565 }
566
Adam Lesinski33dac552015-03-09 15:24:48 -0700567 // Add the app to the list if it is consuming power.
Adam Lesinskie08af192015-03-25 16:42:59 -0700568 if (totalPower != 0 || u.getUid() == 0) {
569 //
570 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
571 //
572 final int uid = app.getUid();
573 final int userId = UserHandle.getUserId(uid);
574 if (uid == Process.WIFI_UID) {
575 mWifiSippers.add(app);
576 } else if (uid == Process.BLUETOOTH_UID) {
577 mBluetoothSippers.add(app);
578 } else if (!forAllUsers && asUsers.get(userId) == null
579 && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
580 // We are told to just report this user's apps as one large entry.
581 List<BatterySipper> list = mUserSippers.get(userId);
582 if (list == null) {
583 list = new ArrayList<>();
584 mUserSippers.put(userId, list);
585 }
586 list.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000587 } else {
Adam Lesinskie08af192015-03-25 16:42:59 -0700588 mUsageList.add(app);
Bart Searse9b9b732015-04-07 06:14:04 +0000589 }
Bart Searse9b9b732015-04-07 06:14:04 +0000590
Adam Lesinskie08af192015-03-25 16:42:59 -0700591 if (uid == 0) {
Adam Lesinski43f222f2015-06-30 12:54:41 -0700592 osSipper = app;
Adam Lesinskie08af192015-03-25 16:42:59 -0700593 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800594 }
595 }
Adam Lesinski43f222f2015-06-30 12:54:41 -0700596
597 if (osSipper != null) {
598 // The device has probably been awake for longer than the screen on
599 // time and application wake lock time would account for. Assign
600 // this remainder to the OS, if possible.
Adam Lesinski76a267b2016-04-20 13:04:59 -0700601 mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
602 mRawUptimeUs, mStatsType);
Adam Lesinski43f222f2015-06-30 12:54:41 -0700603 osSipper.sumPower();
604 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800605 }
606
607 private void addPhoneUsage() {
Adam Lesinski76a267b2016-04-20 13:04:59 -0700608 long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800609 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
jackqdyulei5dad8062017-06-05 16:15:54 -0700610 * phoneOnTimeMs / (60 * 60 * 1000);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800611 if (phoneOnPower != 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700612 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800613 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800614 }
615
Adam Lesinski76a267b2016-04-20 13:04:59 -0700616 /**
617 * Screen power is the additional power the screen takes while the device is running.
618 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800619 private void addScreenUsage() {
620 double power = 0;
Adam Lesinski76a267b2016-04-20 13:04:59 -0700621 long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800622 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
623 final double screenFullPower =
624 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
625 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
626 double screenBinPower = screenFullPower * (i + 0.5f)
627 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
Adam Lesinski76a267b2016-04-20 13:04:59 -0700628 long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType)
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800629 / 1000;
jackqdyulei5dad8062017-06-05 16:15:54 -0700630 double p = screenBinPower * brightnessTime;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800631 if (DEBUG && p != 0) {
632 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
Dianne Hackborn099bc622014-01-22 13:39:16 -0800633 + " power=" + makemAh(p / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800634 }
635 power += p;
636 }
jackqdyulei5dad8062017-06-05 16:15:54 -0700637 power /= (60 * 60 * 1000); // To hours
Dianne Hackborn099bc622014-01-22 13:39:16 -0800638 if (power != 0) {
639 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
640 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800641 }
642
643 private void addRadioUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700644 BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
Adam Lesinski76a267b2016-04-20 13:04:59 -0700645 mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
Adam Lesinskie08af192015-03-25 16:42:59 -0700646 mStatsType);
647 radio.sumPower();
648 if (radio.totalPowerMah > 0) {
649 mUsageList.add(radio);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800650 }
651 }
652
653 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
jackqdyulei5dad8062017-06-05 16:15:54 -0700654 for (int i = 0; i < from.size(); i++) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800655 BatterySipper wbs = from.get(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700656 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700657 bs.add(wbs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800658 }
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800659 bs.computeMobilemspp();
Adam Lesinski57123002015-06-12 16:12:07 -0700660 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800661 }
662
Adam Lesinski76a267b2016-04-20 13:04:59 -0700663 /**
664 * Calculate the baseline power usage for the device when it is in suspend and idle.
665 * The device is drawing POWER_CPU_IDLE power at its lowest power state.
666 * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
667 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800668 private void addIdleUsage() {
Adam Lesinski76a267b2016-04-20 13:04:59 -0700669 final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
670 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
671 final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
672 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
673 final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
674 if (DEBUG && totalPowerMah != 0) {
675 Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
676 + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000)));
677 Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000)
678 + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800679 }
Adam Lesinski76a267b2016-04-20 13:04:59 -0700680
681 if (totalPowerMah != 0) {
682 addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800683 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800684 }
685
Adam Lesinski33dac552015-03-09 15:24:48 -0700686 /**
687 * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
688 * then only the WiFi process gets blamed here since we normalize power calculations and
689 * assign all the power drain to apps. If energy info is not reported, we attribute the
690 * difference between total running time of WiFi for all apps and the actual running time
691 * of WiFi to the WiFi subsystem.
692 */
693 private void addWiFiUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700694 BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
jackqdyulei5dad8062017-06-05 16:15:54 -0700695 mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
696 mStatsType);
Adam Lesinski57123002015-06-12 16:12:07 -0700697 aggregateSippers(bs, mWifiSippers, "WIFI");
698 if (bs.totalPowerMah > 0) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700699 mUsageList.add(bs);
Adam Lesinski33dac552015-03-09 15:24:48 -0700700 }
701 }
702
703 /**
704 * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
705 * Bluetooth Category.
706 */
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800707 private void addBluetoothUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700708 BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
Adam Lesinski76a267b2016-04-20 13:04:59 -0700709 mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
Adam Lesinskie08af192015-03-25 16:42:59 -0700710 mStatsType);
Adam Lesinski57123002015-06-12 16:12:07 -0700711 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
712 if (bs.totalPowerMah > 0) {
Adam Lesinskiee36c282015-05-14 13:57:08 -0700713 mUsageList.add(bs);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800714 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800715 }
716
717 private void addUserUsage() {
Adam Lesinskie08af192015-03-25 16:42:59 -0700718 for (int i = 0; i < mUserSippers.size(); i++) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800719 final int userId = mUserSippers.keyAt(i);
Adam Lesinskie08af192015-03-25 16:42:59 -0700720 BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800721 bs.userId = userId;
Adam Lesinskie08af192015-03-25 16:42:59 -0700722 aggregateSippers(bs, mUserSippers.valueAt(i), "User");
Adam Lesinskie08af192015-03-25 16:42:59 -0700723 mUsageList.add(bs);
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800724 }
725 }
726
James Carr2dd7e5e2016-07-20 18:48:39 -0700727 private void addMemoryUsage() {
728 BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0);
729 mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs,
730 mStatsType);
731 memory.sumPower();
732 if (memory.totalPowerMah > 0) {
733 mUsageList.add(memory);
734 }
735 }
736
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800737 private void processMiscUsage() {
738 addUserUsage();
739 addPhoneUsage();
740 addScreenUsage();
741 addWiFiUsage();
742 addBluetoothUsage();
James Carr2dd7e5e2016-07-20 18:48:39 -0700743 addMemoryUsage();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800744 addIdleUsage(); // Not including cellular idle power
745 // Don't compute radio usage if it's a wifi-only device
Dianne Hackbornd953c532014-08-16 18:17:38 -0700746 if (!mWifiOnly) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800747 addRadioUsage();
748 }
749 }
750
751 private BatterySipper addEntry(DrainType drainType, long time, double power) {
Adam Lesinskie08af192015-03-25 16:42:59 -0700752 BatterySipper bs = new BatterySipper(drainType, null, 0);
753 bs.usagePowerMah = power;
754 bs.usageTimeMs = time;
755 bs.sumPower();
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800756 mUsageList.add(bs);
757 return bs;
758 }
759
760 public List<BatterySipper> getUsageList() {
761 return mUsageList;
762 }
763
Dianne Hackbornd45665b2014-02-26 12:35:32 -0800764 public List<BatterySipper> getMobilemsppList() {
765 return mMobilemsppList;
766 }
767
jackqdyulei5dad8062017-06-05 16:15:54 -0700768 public long getStatsPeriod() {
769 return mStatsPeriod;
770 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800771
jackqdyulei5dad8062017-06-05 16:15:54 -0700772 public int getStatsType() {
773 return mStatsType;
774 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800775
jackqdyulei5dad8062017-06-05 16:15:54 -0700776 public double getMaxPower() {
777 return mMaxPower;
778 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800779
jackqdyulei5dad8062017-06-05 16:15:54 -0700780 public double getMaxRealPower() {
781 return mMaxRealPower;
782 }
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700783
jackqdyulei5dad8062017-06-05 16:15:54 -0700784 public double getTotalPower() {
785 return mTotalPower;
786 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800787
jackqdyulei5dad8062017-06-05 16:15:54 -0700788 public double getComputedPower() {
789 return mComputedPower;
790 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800791
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800792 public double getMinDrainedPower() {
793 return mMinDrainedPower;
794 }
795
796 public double getMaxDrainedPower() {
797 return mMaxDrainedPower;
798 }
799
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700800 public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
801 return readFully(stream, stream.available());
802 }
803
804 public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
805 int pos = 0;
806 byte[] data = new byte[avail];
807 while (true) {
jackqdyulei5dad8062017-06-05 16:15:54 -0700808 int amt = stream.read(data, pos, data.length - pos);
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700809 //Log.i("foo", "Read " + amt + " bytes at " + pos
810 // + " of avail " + data.length);
811 if (amt <= 0) {
812 //Log.i("foo", "**** FINISHED READING: pos=" + pos
813 // + " len=" + data.length);
814 return data;
815 }
816 pos += amt;
817 avail = stream.available();
jackqdyulei5dad8062017-06-05 16:15:54 -0700818 if (avail > data.length - pos) {
819 byte[] newData = new byte[pos + avail];
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700820 System.arraycopy(data, 0, newData, 0, pos);
821 data = newData;
822 }
823 }
824 }
825
jackqdyulei5dad8062017-06-05 16:15:54 -0700826 /**
827 * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on
828 * foreground activity time.
829 *
830 * @param sippers sipper list that need to check and remove
831 * @return the total power of the hidden items of {@link BatterySipper}
832 * for proportional smearing
833 */
834 public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
835 double proportionalSmearPowerMah = 0;
836 BatterySipper screenSipper = null;
837 for (int i = sippers.size() - 1; i >= 0; i--) {
838 final BatterySipper sipper = sippers.get(i);
839 sipper.shouldHide = shouldHideSipper(sipper);
840 if (sipper.shouldHide) {
841 if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
842 && sipper.drainType != BatterySipper.DrainType.SCREEN
843 && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED) {
844 // Don't add it if it is overcounted, unaccounted or screen
845 proportionalSmearPowerMah += sipper.totalPowerMah;
846 }
847 }
848
849 if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
850 screenSipper = sipper;
851 }
852 }
853
854 smearScreenBatterySipper(sippers, screenSipper);
855
856 return proportionalSmearPowerMah;
857 }
858
859 /**
860 * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
861 * time.
862 */
863 public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
864 final long rawRealtimeMs = SystemClock.elapsedRealtime();
865 long totalActivityTimeMs = 0;
866 final SparseLongArray activityTimeArray = new SparseLongArray();
867 for (int i = 0, size = sippers.size(); i < size; i++) {
868 final BatteryStats.Uid uid = sippers.get(i).uidObj;
869 if (uid != null) {
870 final long timeMs = getForegroundActivityTotalTimeMs(uid, rawRealtimeMs);
871 activityTimeArray.put(uid.getUid(), timeMs);
872 totalActivityTimeMs += timeMs;
873 }
874 }
875
876 if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
877 final double screenPowerMah = screenSipper.totalPowerMah;
878 for (int i = 0, size = sippers.size(); i < size; i++) {
879 final BatterySipper sipper = sippers.get(i);
880 sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
881 / totalActivityTimeMs;
882 }
883 }
884 }
885
886 /**
887 * Check whether we should hide the battery sipper.
888 */
889 public boolean shouldHideSipper(BatterySipper sipper) {
890 final BatterySipper.DrainType drainType = sipper.drainType;
891
892 return drainType == BatterySipper.DrainType.IDLE
893 || drainType == BatterySipper.DrainType.CELL
894 || drainType == BatterySipper.DrainType.SCREEN
895 || drainType == BatterySipper.DrainType.UNACCOUNTED
896 || drainType == BatterySipper.DrainType.OVERCOUNTED
897 || isTypeService(sipper)
898 || isTypeSystem(sipper);
899 }
900
901 /**
902 * Check whether {@code sipper} is type service
903 */
904 public boolean isTypeService(BatterySipper sipper) {
905 final String[] packages = mPackageManager.getPackagesForUid(sipper.getUid());
906 if (packages == null) {
907 return false;
908 }
909
910 for (String packageName : packages) {
911 if (ArrayUtils.contains(mServicepackageArray, packageName)) {
912 return true;
913 }
914 }
915
916 return false;
917 }
918
919 /**
920 * Check whether {@code sipper} is type system
921 */
922 public boolean isTypeSystem(BatterySipper sipper) {
923 final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
924 sipper.mPackages = mPackageManager.getPackagesForUid(uid);
925 // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID
926 if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
927 return true;
928 } else if (sipper.mPackages != null) {
929 for (final String packageName : sipper.mPackages) {
930 if (ArrayUtils.contains(mSystemPackageArray, packageName)) {
931 return true;
932 }
933 }
934 }
935
936 return false;
937 }
938
939 @VisibleForTesting
940 public long getForegroundActivityTotalTimeMs(BatteryStats.Uid uid, long rawRealtimeMs) {
941 final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
942 if (timer != null) {
943 return timer.getTotalTimeLocked(rawRealtimeMs, BatteryStats.STATS_SINCE_CHARGED);
944 }
945
946 return 0;
947 }
948
949 @VisibleForTesting
950 public void setPackageManager(PackageManager packageManager) {
951 mPackageManager = packageManager;
952 }
953
954 @VisibleForTesting
955 public void setSystemPackageArray(String[] array) {
956 mSystemPackageArray = array;
957 }
958
959 @VisibleForTesting
960 public void setServicePackageArray(String[] array) {
961 mServicepackageArray = array;
962 }
963
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800964 private void load() {
965 if (mBatteryInfo == null) {
966 return;
967 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700968 mStats = getStats(mBatteryInfo);
Dianne Hackborn2ffa11e2014-04-21 15:56:18 -0700969 if (mCollectBatteryBroadcast) {
970 mBatteryBroadcast = mContext.registerReceiver(null,
971 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
972 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800973 }
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700974
975 private static BatteryStatsImpl getStats(IBatteryStats service) {
976 try {
977 ParcelFileDescriptor pfd = service.getStatisticsStream();
978 if (pfd != null) {
Nimrod Gileadie9be5da2016-02-29 12:33:13 +0000979 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700980 byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
981 Parcel parcel = Parcel.obtain();
982 parcel.unmarshall(data, 0, data.length);
983 parcel.setDataPosition(0);
984 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
985 .createFromParcel(parcel);
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700986 return stats;
987 } catch (IOException e) {
988 Log.w(TAG, "Unable to read statistics stream", e);
989 }
990 }
991 } catch (RemoteException e) {
992 Log.w(TAG, "RemoteException:", e);
993 }
Dianne Hackbornd7c92892014-08-27 16:44:24 -0700994 return new BatteryStatsImpl();
Dianne Hackborn0068d3dc2014-08-06 19:20:25 -0700995 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800996}