blob: 38335b55fbe7af9f3e376e3f05cebe0313e3e364 [file] [log] [blame]
/*
* Copyright (C) 2006-2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import android.os.BatteryStats;
import android.os.NetStat;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* All information we are collecting about things that can happen that impact
* battery life. All times are represented in microseconds except where indicated
* otherwise.
*/
public final class BatteryStatsImpl extends BatteryStats {
private static final String TAG = "BatteryStatsImpl";
private static final boolean DEBUG = false;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
private static final int VERSION = 28;
private final File mFile;
private final File mBackupFile;
/**
* The statistics we have collected organized by uids.
*/
final SparseArray<BatteryStatsImpl.Uid> mUidStats =
new SparseArray<BatteryStatsImpl.Uid>();
// A set of pools of currently active timers. When a timer is queried, we will divide the
// elapsed time by the number of active timers to arrive at that timer's share of the time.
// In order to do this, we must refresh each timer whenever the number of active timers
// changes.
final ArrayList<Timer> mPartialTimers = new ArrayList<Timer>();
final ArrayList<Timer> mFullTimers = new ArrayList<Timer>();
final ArrayList<Timer> mWindowTimers = new ArrayList<Timer>();
final SparseArray<ArrayList<Timer>> mSensorTimers
= new SparseArray<ArrayList<Timer>>();
// These are the objects that will want to do something when the device
// is unplugged from power.
final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
int mStartCount;
long mBatteryUptime;
long mBatteryLastUptime;
long mBatteryRealtime;
long mBatteryLastRealtime;
long mUptime;
long mUptimeStart;
long mLastUptime;
long mRealtime;
long mRealtimeStart;
long mLastRealtime;
boolean mScreenOn;
Timer mScreenOnTimer;
boolean mPhoneOn;
Timer mPhoneOnTimer;
boolean mWifiOn;
Timer mWifiOnTimer;
boolean mWifiRunning;
Timer mWifiRunningTimer;
boolean mBluetoothOn;
Timer mBluetoothOnTimer;
/**
* These provide time bases that discount the time the device is plugged
* in to power.
*/
boolean mOnBattery;
boolean mOnBatteryInternal;
long mTrackBatteryPastUptime;
long mTrackBatteryUptimeStart;
long mTrackBatteryPastRealtime;
long mTrackBatteryRealtimeStart;
long mUnpluggedBatteryUptime;
long mUnpluggedBatteryRealtime;
/*
* These keep track of battery levels (1-100) at the last plug event and the last unplug event.
*/
int mUnpluggedStartLevel;
int mPluggedStartLevel;
long mLastWriteTime = 0; // Milliseconds
// For debugging
public BatteryStatsImpl() {
mFile = mBackupFile = null;
}
public static interface Unpluggable {
void unplug(long batteryUptime, long batteryRealtime);
void plug(long batteryUptime, long batteryRealtime);
}
/**
* State for keeping track of timing information.
*/
public static final class Timer extends BatteryStats.Timer implements Unpluggable {
final int mType;
final ArrayList<Timer> mTimerPool;
int mNesting;
int mCount;
int mLoadedCount;
int mLastCount;
int mUnpluggedCount;
// Times are in microseconds for better accuracy when dividing by the
// lock count, and are in "battery realtime" units.
/**
* The total time we have accumulated since the start of the original
* boot, to the last time something interesting happened in the
* current run.
*/
long mTotalTime;
/**
* The total time we loaded for the previous runs. Subtract this from
* mTotalTime to find the time for the current run of the system.
*/
long mLoadedTime;
/**
* The run time of the last run of the system, as loaded from the
* saved data.
*/
long mLastTime;
/**
* The value of mTotalTime when unplug() was last called. Subtract
* this from mTotalTime to find the time since the last unplug from
* power.
*/
long mUnpluggedTime;
/**
* The last time at which we updated the timer. If mNesting is > 0,
* subtract this from the current battery time to find the amount of
* time we have been running since we last computed an update.
*/
long mUpdateTime;
/**
* The total time at which the timer was acquired, to determine if
* was actually held for an interesting duration.
*/
long mAcquireTime;
Timer(int type, ArrayList<Timer> timerPool,
ArrayList<Unpluggable> unpluggables, Parcel in) {
mType = type;
mTimerPool = timerPool;
mCount = in.readInt();
mLoadedCount = in.readInt();
mLastCount = in.readInt();
mUnpluggedCount = in.readInt();
mTotalTime = in.readLong();
mLoadedTime = in.readLong();
mLastTime = in.readLong();
mUpdateTime = in.readLong();
mUnpluggedTime = in.readLong();
unpluggables.add(this);
}
Timer(int type, ArrayList<Timer> timerPool,
ArrayList<Unpluggable> unpluggables) {
mType = type;
mTimerPool = timerPool;
unpluggables.add(this);
}
public void writeToParcel(Parcel out, long batteryRealtime) {
out.writeInt(mCount);
out.writeInt(mLoadedCount);
out.writeInt(mLastCount);
out.writeInt(mUnpluggedCount);
out.writeLong(computeRunTimeLocked(batteryRealtime));
out.writeLong(mLoadedTime);
out.writeLong(mLastTime);
out.writeLong(mUpdateTime);
out.writeLong(mUnpluggedTime);
}
public void unplug(long batteryUptime, long batteryRealtime) {
if (DEBUG && mType < 0) {
Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime
+ " old mUnpluggedTime=" + mUnpluggedTime
+ " old mUnpluggedCount=" + mUnpluggedCount);
}
mUnpluggedTime = computeRunTimeLocked(batteryRealtime);
mUnpluggedCount = mCount;
if (DEBUG && mType < 0) {
Log.v(TAG, "unplug #" + mType
+ ": new mUnpluggedTime=" + mUnpluggedTime
+ " new mUnpluggedCount=" + mUnpluggedCount);
}
}
public void plug(long batteryUptime, long batteryRealtime) {
if (mNesting > 0) {
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime
+ " old mTotalTime=" + mTotalTime
+ " old mUpdateTime=" + mUpdateTime);
}
mTotalTime = computeRunTimeLocked(batteryRealtime);
mUpdateTime = batteryRealtime;
if (DEBUG && mType < 0) {
Log.v(TAG, "plug #" + mType
+ ": new mTotalTime=" + mTotalTime
+ " old mUpdateTime=" + mUpdateTime);
}
}
}
/**
* Writes a possibly null Timer to a Parcel.
*
* @param out the Parcel to be written to.
* @param timer a Timer, or null.
*/
public static void writeTimerToParcel(Parcel out, Timer timer,
long batteryRealtime) {
if (timer == null) {
out.writeInt(0); // indicates null
return;
}
out.writeInt(1); // indicates non-null
timer.writeToParcel(out, batteryRealtime);
}
@Override
public long getTotalTime(long batteryRealtime, int which) {
long val;
if (which == STATS_LAST) {
val = mLastTime;
} else {
val = computeRunTimeLocked(batteryRealtime);
if (which == STATS_UNPLUGGED) {
val -= mUnpluggedTime;
} else if (which != STATS_TOTAL) {
val -= mLoadedTime;
}
}
return val;
}
@Override
public int getCount(int which) {
int val;
if (which == STATS_LAST) {
val = mLastCount;
} else {
val = mCount;
if (which == STATS_UNPLUGGED) {
val -= mUnpluggedCount;
} else if (which != STATS_TOTAL) {
val -= mLoadedCount;
}
}
return val;
}
public void logState() {
Log.i("foo", "mNesting=" + mNesting + " mCount=" + mCount
+ " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ " mUnpluggedCount=" + mUnpluggedCount);
Log.i("foo", "mTotalTime=" + mTotalTime
+ " mLoadedTime=" + mLoadedTime);
Log.i("foo", "mLastTime=" + mLastTime
+ " mUnpluggedTime=" + mUnpluggedTime);
Log.i("foo", "mUpdateTime=" + mUpdateTime
+ " mAcquireTime=" + mAcquireTime);
}
void startRunningLocked(BatteryStatsImpl stats) {
if (mNesting++ == 0) {
mUpdateTime = stats.getBatteryRealtimeLocked(
SystemClock.elapsedRealtime() * 1000);
if (mTimerPool != null) {
// Accumulate time to all currently active timers before adding
// this new one to the pool.
refreshTimersLocked(stats, mTimerPool);
// Add this timer to the active pool
mTimerPool.add(this);
}
// Increment the count
mCount++;
mAcquireTime = mTotalTime;
if (DEBUG && mType < 0) {
Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTime
+ " mTotalTime=" + mTotalTime + " mCount=" + mCount
+ " mAcquireTime=" + mAcquireTime);
}
}
}
void stopRunningLocked(BatteryStatsImpl stats) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
return;
}
if (--mNesting == 0) {
if (mTimerPool != null) {
// Accumulate time to all active counters, scaled by the total
// active in the pool, before taking this one out of the pool.
refreshTimersLocked(stats, mTimerPool);
// Remove this timer from the active pool
mTimerPool.remove(this);
} else {
final long realtime = SystemClock.elapsedRealtime() * 1000;
final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
mNesting = 1;
mTotalTime = computeRunTimeLocked(batteryRealtime);
mNesting = 0;
}
if (DEBUG && mType < 0) {
Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime
+ " mTotalTime=" + mTotalTime + " mCount=" + mCount
+ " mAcquireTime=" + mAcquireTime);
}
if (mTotalTime == mAcquireTime) {
// If there was no change in the time, then discard this
// count. A somewhat cheezy strategy, but hey.
mCount--;
}
}
}
// Update the total time for all other running Timers with the same type as this Timer
// due to a change in timer count
private static void refreshTimersLocked(final BatteryStatsImpl stats,
final ArrayList<Timer> pool) {
final long realtime = SystemClock.elapsedRealtime() * 1000;
final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
final int N = pool.size();
for (int i=N-1; i>= 0; i--) {
final Timer t = pool.get(i);
long heldTime = batteryRealtime - t.mUpdateTime;
if (heldTime > 0) {
t.mTotalTime += heldTime / N;
}
t.mUpdateTime = batteryRealtime;
}
}
private long computeRunTimeLocked(long curBatteryRealtime) {
return mTotalTime + (mNesting > 0
? (curBatteryRealtime - mUpdateTime)
/ (mTimerPool != null ? mTimerPool.size() : 1)
: 0);
}
void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
long runTime = computeRunTimeLocked(batteryRealtime);
// Divide by 1000 for backwards compatibility
out.writeLong((runTime + 500) / 1000);
out.writeLong(((runTime - mLoadedTime) + 500) / 1000);
out.writeInt(mCount);
out.writeInt(mCount - mLoadedCount);
}
void readSummaryFromParcelLocked(Parcel in) {
// Multiply by 1000 for backwards compatibility
mTotalTime = mLoadedTime = in.readLong() * 1000;
mLastTime = in.readLong() * 1000;
mUnpluggedTime = mTotalTime;
mCount = mLoadedCount = in.readInt();
mLastCount = in.readInt();
mUnpluggedCount = mCount;
mNesting = 0;
}
}
public void doUnplug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
u.mStartedTcpBytesReceived = NetStat.getUidRxBytes(u.mUid);
u.mStartedTcpBytesSent = NetStat.getUidTxBytes(u.mUid);
u.mTcpBytesReceivedAtLastUnplug = u.mCurrentTcpBytesReceived;
u.mTcpBytesSentAtLastUnplug = u.mCurrentTcpBytesSent;
}
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
}
}
public void doPlug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
if (u.mStartedTcpBytesReceived >= 0) {
u.mCurrentTcpBytesReceived = u.computeCurrentTcpBytesReceived();
u.mStartedTcpBytesReceived = -1;
}
if (u.mStartedTcpBytesSent >= 0) {
u.mCurrentTcpBytesSent = u.computeCurrentTcpBytesSent();
u.mStartedTcpBytesSent = -1;
}
}
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
}
}
public void noteStartGps(int uid) {
mUidStats.get(uid).noteStartGps();
}
public void noteStopGps(int uid) {
mUidStats.get(uid).noteStopGps();
}
public void noteScreenOnLocked() {
if (!mScreenOn) {
mScreenOn = true;
mScreenOnTimer.startRunningLocked(this);
}
}
public void noteScreenOffLocked() {
if (mScreenOn) {
mScreenOn = false;
mScreenOnTimer.stopRunningLocked(this);
}
}
public void notePhoneOnLocked() {
if (!mPhoneOn) {
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(this);
}
}
public void notePhoneOffLocked() {
if (mPhoneOn) {
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(this);
}
}
public void noteWifiOnLocked() {
if (!mWifiOn) {
mWifiOn = true;
mWifiOnTimer.startRunningLocked(this);
}
}
public void noteWifiOffLocked() {
if (mWifiOn) {
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(this);
}
}
public void noteWifiRunningLocked() {
if (!mWifiRunning) {
mWifiRunning = true;
mWifiRunningTimer.startRunningLocked(this);
}
}
public void noteWifiStoppedLocked() {
if (mWifiRunning) {
mWifiRunning = false;
mWifiRunningTimer.stopRunningLocked(this);
}
}
public void noteBluetoothOnLocked() {
if (!mBluetoothOn) {
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(this);
}
}
public void noteBluetoothOffLocked() {
if (mBluetoothOn) {
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(this);
}
}
public void noteFullWifiLockAcquiredLocked(int uid) {
Uid u = mUidStats.get(uid);
if (u != null) {
u.noteFullWifiLockAcquiredLocked();
}
}
public void noteFullWifiLockReleasedLocked(int uid) {
Uid u = mUidStats.get(uid);
if (u != null) {
u.noteFullWifiLockReleasedLocked();
}
}
public void noteScanWifiLockAcquiredLocked(int uid) {
Uid u = mUidStats.get(uid);
if (u != null) {
u.noteScanWifiLockAcquiredLocked();
}
}
public void noteScanWifiLockReleasedLocked(int uid) {
Uid u = mUidStats.get(uid);
if (u != null) {
u.noteScanWifiLockReleasedLocked();
}
}
@Override public long getScreenOnTime(long batteryRealtime, int which) {
return mScreenOnTimer.getTotalTime(batteryRealtime, which);
}
@Override public long getPhoneOnTime(long batteryRealtime, int which) {
return mPhoneOnTimer.getTotalTime(batteryRealtime, which);
}
@Override public long getWifiOnTime(long batteryRealtime, int which) {
return mWifiOnTimer.getTotalTime(batteryRealtime, which);
}
@Override public long getWifiRunningTime(long batteryRealtime, int which) {
return mWifiRunningTimer.getTotalTime(batteryRealtime, which);
}
@Override public long getBluetoothOnTime(long batteryRealtime, int which) {
return mBluetoothOnTimer.getTotalTime(batteryRealtime, which);
}
@Override public boolean getIsOnBattery() {
return mOnBattery;
}
@Override public SparseArray<? extends BatteryStats.Uid> getUidStats() {
return mUidStats;
}
/**
* The statistics associated with a particular uid.
*/
public final class Uid extends BatteryStats.Uid {
final int mUid;
long mLoadedTcpBytesReceived;
long mLoadedTcpBytesSent;
long mCurrentTcpBytesReceived;
long mCurrentTcpBytesSent;
long mTcpBytesReceivedAtLastUnplug;
long mTcpBytesSentAtLastUnplug;
// These are not saved/restored when parcelling, since we want
// to return from the parcel with a snapshot of the state.
long mStartedTcpBytesReceived = -1;
long mStartedTcpBytesSent = -1;
boolean mFullWifiLockOut;
Timer mFullWifiLockTimer;
boolean mScanWifiLockOut;
Timer mScanWifiLockTimer;
/**
* The statistics we have collected for this uid's wake locks.
*/
final HashMap<String, Wakelock> mWakelockStats = new HashMap<String, Wakelock>();
/**
* The statistics we have collected for this uid's sensor activations.
*/
final HashMap<Integer, Sensor> mSensorStats = new HashMap<Integer, Sensor>();
/**
* The statistics we have collected for this uid's processes.
*/
final HashMap<String, Proc> mProcessStats = new HashMap<String, Proc>();
/**
* The statistics we have collected for this uid's processes.
*/
final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
public Uid(int uid) {
mUid = uid;
mFullWifiLockTimer = new Timer(FULL_WIFI_LOCK, null, mUnpluggables);
mScanWifiLockTimer = new Timer(SCAN_WIFI_LOCK, null, mUnpluggables);
}
@Override
public Map<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
return mWakelockStats;
}
@Override
public Map<Integer, ? extends BatteryStats.Uid.Sensor> getSensorStats() {
return mSensorStats;
}
@Override
public Map<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
return mProcessStats;
}
@Override
public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
return mPackageStats;
}
public int getUid() {
return mUid;
}
public long getTcpBytesReceived(int which) {
if (which == STATS_LAST) {
return mLoadedTcpBytesReceived;
} else {
long current = computeCurrentTcpBytesReceived();
if (which == STATS_UNPLUGGED) {
current -= mTcpBytesReceivedAtLastUnplug;
} else if (which == STATS_TOTAL) {
current += mLoadedTcpBytesReceived;
}
return current;
}
}
public long computeCurrentTcpBytesReceived() {
return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
? (NetStat.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0);
}
public long getTcpBytesSent(int which) {
if (which == STATS_LAST) {
return mLoadedTcpBytesSent;
} else {
long current = computeCurrentTcpBytesSent();
if (which == STATS_UNPLUGGED) {
current -= mTcpBytesSentAtLastUnplug;
} else if (which == STATS_TOTAL) {
current += mLoadedTcpBytesSent;
}
return current;
}
}
@Override
public void noteFullWifiLockAcquiredLocked() {
if (!mFullWifiLockOut) {
mFullWifiLockOut = true;
mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
}
}
@Override
public void noteFullWifiLockReleasedLocked() {
if (mFullWifiLockOut) {
mFullWifiLockOut = false;
mFullWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this);
}
}
@Override
public void noteScanWifiLockAcquiredLocked() {
if (!mScanWifiLockOut) {
mScanWifiLockOut = true;
mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
}
}
@Override
public void noteScanWifiLockReleasedLocked() {
if (mScanWifiLockOut) {
mScanWifiLockOut = false;
mScanWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this);
}
}
@Override
public long getFullWifiLockTime(long batteryRealtime, int which) {
return mFullWifiLockTimer.getTotalTime(batteryRealtime, which);
}
@Override
public long getScanWifiLockTime(long batteryRealtime, int which) {
return mScanWifiLockTimer.getTotalTime(batteryRealtime, which);
}
public long computeCurrentTcpBytesSent() {
return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0
? (NetStat.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0);
}
void writeToParcelLocked(Parcel out, long batteryRealtime) {
out.writeInt(mWakelockStats.size());
for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
out.writeString(wakelockEntry.getKey());
Uid.Wakelock wakelock = wakelockEntry.getValue();
wakelock.writeToParcelLocked(out, batteryRealtime);
}
out.writeInt(mSensorStats.size());
for (Map.Entry<Integer, Uid.Sensor> sensorEntry : mSensorStats.entrySet()) {
out.writeInt(sensorEntry.getKey());
Uid.Sensor sensor = sensorEntry.getValue();
sensor.writeToParcelLocked(out, batteryRealtime);
}
out.writeInt(mProcessStats.size());
for (Map.Entry<String, Uid.Proc> procEntry : mProcessStats.entrySet()) {
out.writeString(procEntry.getKey());
Uid.Proc proc = procEntry.getValue();
proc.writeToParcelLocked(out);
}
out.writeInt(mPackageStats.size());
for (Map.Entry<String, Uid.Pkg> pkgEntry : mPackageStats.entrySet()) {
out.writeString(pkgEntry.getKey());
Uid.Pkg pkg = pkgEntry.getValue();
pkg.writeToParcelLocked(out);
}
out.writeLong(mLoadedTcpBytesReceived);
out.writeLong(mLoadedTcpBytesSent);
out.writeLong(computeCurrentTcpBytesReceived());
out.writeLong(computeCurrentTcpBytesSent());
out.writeLong(mTcpBytesReceivedAtLastUnplug);
out.writeLong(mTcpBytesSentAtLastUnplug);
mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
mScanWifiLockTimer.writeToParcel(out, batteryRealtime);
}
void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
int numWakelocks = in.readInt();
mWakelockStats.clear();
for (int j = 0; j < numWakelocks; j++) {
String wakelockName = in.readString();
Uid.Wakelock wakelock = new Wakelock();
wakelock.readFromParcelLocked(unpluggables, in);
mWakelockStats.put(wakelockName, wakelock);
}
int numSensors = in.readInt();
mSensorStats.clear();
for (int k = 0; k < numSensors; k++) {
int sensorNumber = in.readInt();
Uid.Sensor sensor = new Sensor(sensorNumber);
sensor.readFromParcelLocked(mUnpluggables, in);
mSensorStats.put(sensorNumber, sensor);
}
int numProcs = in.readInt();
mProcessStats.clear();
for (int k = 0; k < numProcs; k++) {
String processName = in.readString();
Uid.Proc proc = new Proc();
proc.readFromParcelLocked(in);
mProcessStats.put(processName, proc);
}
int numPkgs = in.readInt();
mPackageStats.clear();
for (int l = 0; l < numPkgs; l++) {
String packageName = in.readString();
Uid.Pkg pkg = new Pkg();
pkg.readFromParcelLocked(in);
mPackageStats.put(packageName, pkg);
}
mLoadedTcpBytesReceived = in.readLong();
mLoadedTcpBytesSent = in.readLong();
mCurrentTcpBytesReceived = in.readLong();
mCurrentTcpBytesSent = in.readLong();
mTcpBytesReceivedAtLastUnplug = in.readLong();
mTcpBytesSentAtLastUnplug = in.readLong();
mFullWifiLockOut = false;
mFullWifiLockTimer = new Timer(FULL_WIFI_LOCK, null, mUnpluggables, in);
mScanWifiLockOut = false;
mScanWifiLockTimer = new Timer(SCAN_WIFI_LOCK, null, mUnpluggables, in);
}
/**
* The statistics associated with a particular wake lock.
*/
public final class Wakelock extends BatteryStats.Uid.Wakelock {
/**
* How long (in ms) this uid has been keeping the device partially awake.
*/
Timer mTimerPartial;
/**
* How long (in ms) this uid has been keeping the device fully awake.
*/
Timer mTimerFull;
/**
* How long (in ms) this uid has had a window keeping the device awake.
*/
Timer mTimerWindow;
/**
* Reads a possibly null Timer from a Parcel. The timer is associated with the
* proper timer pool from the given BatteryStatsImpl object.
*
* @param in the Parcel to be read from.
* return a new Timer, or null.
*/
private Timer readTimerFromParcel(int type, ArrayList<Timer> pool,
ArrayList<Unpluggable> unpluggables, Parcel in) {
if (in.readInt() == 0) {
return null;
}
return new Timer(type, pool, unpluggables, in);
}
void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
mPartialTimers, unpluggables, in);
mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL,
mFullTimers, unpluggables, in);
mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW,
mWindowTimers, unpluggables, in);
}
void writeToParcelLocked(Parcel out, long batteryRealtime) {
Timer.writeTimerToParcel(out, mTimerPartial, batteryRealtime);
Timer.writeTimerToParcel(out, mTimerFull, batteryRealtime);
Timer.writeTimerToParcel(out, mTimerWindow, batteryRealtime);
}
@Override
public Timer getWakeTime(int type) {
switch (type) {
case WAKE_TYPE_FULL: return mTimerFull;
case WAKE_TYPE_PARTIAL: return mTimerPartial;
case WAKE_TYPE_WINDOW: return mTimerWindow;
default: throw new IllegalArgumentException("type = " + type);
}
}
}
public final class Sensor extends BatteryStats.Uid.Sensor {
final int mHandle;
Timer mTimer;
public Sensor(int handle) {
mHandle = handle;
}
private Timer readTimerFromParcel(ArrayList<Unpluggable> unpluggables,
Parcel in) {
if (in.readInt() == 0) {
return null;
}
ArrayList<Timer> pool = mSensorTimers.get(mHandle);
if (pool == null) {
pool = new ArrayList<Timer>();
mSensorTimers.put(mHandle, pool);
}
return new Timer(0, pool, unpluggables, in);
}
void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
mTimer = readTimerFromParcel(unpluggables, in);
}
void writeToParcelLocked(Parcel out, long batteryRealtime) {
Timer.writeTimerToParcel(out, mTimer, batteryRealtime);
}
@Override
public Timer getSensorTime() {
return mTimer;
}
public int getHandle() {
return mHandle;
}
}
/**
* The statistics associated with a particular process.
*/
public final class Proc extends BatteryStats.Uid.Proc implements Unpluggable {
/**
* Total time (in 1/100 sec) spent executing in user code.
*/
long mUserTime;
/**
* Total time (in 1/100 sec) spent executing in kernel code.
*/
long mSystemTime;
/**
* Number of times the process has been started.
*/
int mStarts;
/**
* The amount of user time loaded from a previous save.
*/
long mLoadedUserTime;
/**
* The amount of system time loaded from a previous save.
*/
long mLoadedSystemTime;
/**
* The number of times the process has started from a previous save.
*/
int mLoadedStarts;
/**
* The amount of user time loaded from the previous run.
*/
long mLastUserTime;
/**
* The amount of system time loaded from the previous run.
*/
long mLastSystemTime;
/**
* The number of times the process has started from the previous run.
*/
int mLastStarts;
/**
* The amount of user time when last unplugged.
*/
long mUnpluggedUserTime;
/**
* The amount of system time when last unplugged.
*/
long mUnpluggedSystemTime;
/**
* The number of times the process has started before unplugged.
*/
int mUnpluggedStarts;
Proc() {
mUnpluggables.add(this);
}
public void unplug(long batteryUptime, long batteryRealtime) {
mUnpluggedUserTime = mUserTime;
mUnpluggedSystemTime = mSystemTime;
mUnpluggedStarts = mStarts;
}
public void plug(long batteryUptime, long batteryRealtime) {
}
void writeToParcelLocked(Parcel out) {
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
out.writeLong(mUserTime);
out.writeLong(mSystemTime);
out.writeInt(mStarts);
out.writeLong(mLoadedUserTime);
out.writeLong(mLoadedSystemTime);
out.writeInt(mLoadedStarts);
out.writeLong(mLastUserTime);
out.writeLong(mLastSystemTime);
out.writeInt(mLastStarts);
out.writeLong(mUnpluggedUserTime);
out.writeLong(mUnpluggedSystemTime);
out.writeInt(mUnpluggedStarts);
}
void readFromParcelLocked(Parcel in) {
mUserTime = in.readLong();
mSystemTime = in.readLong();
mStarts = in.readInt();
mLoadedUserTime = in.readLong();
mLoadedSystemTime = in.readLong();
mLoadedStarts = in.readInt();
mLastUserTime = in.readLong();
mLastSystemTime = in.readLong();
mLastStarts = in.readInt();
mUnpluggedUserTime = in.readLong();
mUnpluggedSystemTime = in.readLong();
mUnpluggedStarts = in.readInt();
}
public BatteryStatsImpl getBatteryStats() {
return BatteryStatsImpl.this;
}
public void addCpuTimeLocked(int utime, int stime) {
mUserTime += utime;
mSystemTime += stime;
}
public void incStartsLocked() {
mStarts++;
}
@Override
public long getUserTime(int which) {
long val;
if (which == STATS_LAST) {
val = mLastUserTime;
} else {
val = mUserTime;
if (which == STATS_CURRENT) {
val -= mLoadedUserTime;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedUserTime;
}
}
return val;
}
@Override
public long getSystemTime(int which) {
long val;
if (which == STATS_LAST) {
val = mLastSystemTime;
} else {
val = mSystemTime;
if (which == STATS_CURRENT) {
val -= mLoadedSystemTime;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedSystemTime;
}
}
return val;
}
@Override
public int getStarts(int which) {
int val;
if (which == STATS_LAST) {
val = mLastStarts;
} else {
val = mStarts;
if (which == STATS_CURRENT) {
val -= mLoadedStarts;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedStarts;
}
}
return val;
}
}
/**
* The statistics associated with a particular package.
*/
public final class Pkg extends BatteryStats.Uid.Pkg implements Unpluggable {
/**
* Number of times this package has done something that could wake up the
* device from sleep.
*/
int mWakeups;
/**
* Number of things that could wake up the device loaded from a
* previous save.
*/
int mLoadedWakeups;
/**
* Number of things that could wake up the device as of the
* last run.
*/
int mLastWakeups;
/**
* Number of things that could wake up the device as of the
* last run.
*/
int mUnpluggedWakeups;
/**
* The statics we have collected for this package's services.
*/
final HashMap<String, Serv> mServiceStats = new HashMap<String, Serv>();
Pkg() {
mUnpluggables.add(this);
}
public void unplug(long batteryUptime, long batteryRealtime) {
mUnpluggedWakeups = mWakeups;
}
public void plug(long batteryUptime, long batteryRealtime) {
}
void readFromParcelLocked(Parcel in) {
mWakeups = in.readInt();
mLoadedWakeups = in.readInt();
mLastWakeups = in.readInt();
mUnpluggedWakeups = in.readInt();
int numServs = in.readInt();
mServiceStats.clear();
for (int m = 0; m < numServs; m++) {
String serviceName = in.readString();
Uid.Pkg.Serv serv = new Serv();
mServiceStats.put(serviceName, serv);
serv.readFromParcelLocked(in);
}
}
void writeToParcelLocked(Parcel out) {
out.writeInt(mWakeups);
out.writeInt(mLoadedWakeups);
out.writeInt(mLastWakeups);
out.writeInt(mUnpluggedWakeups);
out.writeInt(mServiceStats.size());
for (Map.Entry<String, Uid.Pkg.Serv> servEntry : mServiceStats.entrySet()) {
out.writeString(servEntry.getKey());
Uid.Pkg.Serv serv = servEntry.getValue();
serv.writeToParcelLocked(out);
}
}
@Override
public Map<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
return mServiceStats;
}
@Override
public int getWakeups(int which) {
int val;
if (which == STATS_LAST) {
val = mLastWakeups;
} else {
val = mWakeups;
if (which == STATS_CURRENT) {
val -= mLoadedWakeups;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedWakeups;
}
}
return val;
}
/**
* The statistics associated with a particular service.
*/
public final class Serv extends BatteryStats.Uid.Pkg.Serv implements Unpluggable {
/**
* Total time (ms in battery uptime) the service has been left started.
*/
long mStartTime;
/**
* If service has been started and not yet stopped, this is
* when it was started.
*/
long mRunningSince;
/**
* True if we are currently running.
*/
boolean mRunning;
/**
* Total number of times startService() has been called.
*/
int mStarts;
/**
* Total time (ms in battery uptime) the service has been left launched.
*/
long mLaunchedTime;
/**
* If service has been launched and not yet exited, this is
* when it was launched (ms in battery uptime).
*/
long mLaunchedSince;
/**
* True if we are currently launched.
*/
boolean mLaunched;
/**
* Total number times the service has been launched.
*/
int mLaunches;
/**
* The amount of time spent started loaded from a previous save
* (ms in battery uptime).
*/
long mLoadedStartTime;
/**
* The number of starts loaded from a previous save.
*/
int mLoadedStarts;
/**
* The number of launches loaded from a previous save.
*/
int mLoadedLaunches;
/**
* The amount of time spent started as of the last run (ms
* in battery uptime).
*/
long mLastStartTime;
/**
* The number of starts as of the last run.
*/
int mLastStarts;
/**
* The number of launches as of the last run.
*/
int mLastLaunches;
/**
* The amount of time spent started when last unplugged (ms
* in battery uptime).
*/
long mUnpluggedStartTime;
/**
* The number of starts when last unplugged.
*/
int mUnpluggedStarts;
/**
* The number of launches when last unplugged.
*/
int mUnpluggedLaunches;
Serv() {
mUnpluggables.add(this);
}
public void unplug(long batteryUptime, long batteryRealtime) {
mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime);
mUnpluggedStarts = mStarts;
mUnpluggedLaunches = mLaunches;
}
public void plug(long batteryUptime, long batteryRealtime) {
}
void readFromParcelLocked(Parcel in) {
mStartTime = in.readLong();
mRunningSince = in.readLong();
mRunning = in.readInt() != 0;
mStarts = in.readInt();
mLaunchedTime = in.readLong();
mLaunchedSince = in.readLong();
mLaunched = in.readInt() != 0;
mLaunches = in.readInt();
mLoadedStartTime = in.readLong();
mLoadedStarts = in.readInt();
mLoadedLaunches = in.readInt();
mLastStartTime = in.readLong();
mLastStarts = in.readInt();
mLastLaunches = in.readInt();
mUnpluggedStartTime = in.readLong();
mUnpluggedStarts = in.readInt();
mUnpluggedLaunches = in.readInt();
}
void writeToParcelLocked(Parcel out) {
out.writeLong(mStartTime);
out.writeLong(mRunningSince);
out.writeInt(mRunning ? 1 : 0);
out.writeInt(mStarts);
out.writeLong(mLaunchedTime);
out.writeLong(mLaunchedSince);
out.writeInt(mLaunched ? 1 : 0);
out.writeInt(mLaunches);
out.writeLong(mLoadedStartTime);
out.writeInt(mLoadedStarts);
out.writeInt(mLoadedLaunches);
out.writeLong(mLastStartTime);
out.writeInt(mLastStarts);
out.writeInt(mLastLaunches);
out.writeLong(mUnpluggedStartTime);
out.writeInt(mUnpluggedStarts);
out.writeInt(mUnpluggedLaunches);
}
long getLaunchTimeToNowLocked(long batteryUptime) {
if (!mLaunched) return mLaunchedTime;
return mLaunchedTime + batteryUptime - mLaunchedSince;
}
long getStartTimeToNowLocked(long batteryUptime) {
if (!mRunning) return mStartTime;
return mStartTime + batteryUptime - mRunningSince;
}
public void startLaunchedLocked() {
if (!mLaunched) {
mLaunches++;
mLaunchedSince = getBatteryUptimeLocked();
mLaunched = true;
}
}
public void stopLaunchedLocked() {
if (mLaunched) {
long time = getBatteryUptimeLocked() - mLaunchedSince;
if (time > 0) {
mLaunchedTime += time;
} else {
mLaunches--;
}
mLaunched = false;
}
}
public void startRunningLocked() {
if (!mRunning) {
mStarts++;
mRunningSince = getBatteryUptimeLocked();
mRunning = true;
}
}
public void stopRunningLocked() {
if (mRunning) {
long time = getBatteryUptimeLocked() - mRunningSince;
if (time > 0) {
mStartTime += time;
} else {
mStarts--;
}
mRunning = false;
}
}
public BatteryStatsImpl getBatteryStats() {
return BatteryStatsImpl.this;
}
@Override
public int getLaunches(int which) {
int val;
if (which == STATS_LAST) {
val = mLastLaunches;
} else {
val = mLaunches;
if (which == STATS_CURRENT) {
val -= mLoadedLaunches;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedLaunches;
}
}
return val;
}
@Override
public long getStartTime(long now, int which) {
long val;
if (which == STATS_LAST) {
val = mLastStartTime;
} else {
val = getStartTimeToNowLocked(now);
if (which == STATS_CURRENT) {
val -= mLoadedStartTime;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedStartTime;
}
}
return val;
}
@Override
public int getStarts(int which) {
int val;
if (which == STATS_LAST) {
val = mLastStarts;
} else {
val = mStarts;
if (which == STATS_CURRENT) {
val -= mLoadedStarts;
} else if (which == STATS_UNPLUGGED) {
val -= mUnpluggedStarts;
}
}
return val;
}
}
public BatteryStatsImpl getBatteryStats() {
return BatteryStatsImpl.this;
}
public void incWakeupsLocked() {
mWakeups++;
}
final Serv newServiceStatsLocked() {
return new Serv();
}
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
public Proc getProcessStatsLocked(String name) {
Proc ps = mProcessStats.get(name);
if (ps == null) {
ps = new Proc();
mProcessStats.put(name, ps);
}
return ps;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
public Pkg getPackageStatsLocked(String name) {
Pkg ps = mPackageStats.get(name);
if (ps == null) {
ps = new Pkg();
mPackageStats.put(name, ps);
}
return ps;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
public Pkg.Serv getServiceStatsLocked(String pkg, String serv) {
Pkg ps = getPackageStatsLocked(pkg);
Pkg.Serv ss = ps.mServiceStats.get(serv);
if (ss == null) {
ss = ps.newServiceStatsLocked();
ps.mServiceStats.put(serv, ss);
}
return ss;
}
public Timer getWakeTimerLocked(String name, int type) {
Wakelock wl = mWakelockStats.get(name);
if (wl == null) {
wl = new Wakelock();
mWakelockStats.put(name, wl);
}
Timer t = null;
switch (type) {
case WAKE_TYPE_PARTIAL:
t = wl.mTimerPartial;
if (t == null) {
t = new Timer(WAKE_TYPE_PARTIAL, mPartialTimers, mUnpluggables);
wl.mTimerPartial = t;
}
return t;
case WAKE_TYPE_FULL:
t = wl.mTimerFull;
if (t == null) {
t = new Timer(WAKE_TYPE_FULL, mFullTimers, mUnpluggables);
wl.mTimerFull = t;
}
return t;
case WAKE_TYPE_WINDOW:
t = wl.mTimerWindow;
if (t == null) {
t = new Timer(WAKE_TYPE_WINDOW, mWindowTimers, mUnpluggables);
wl.mTimerWindow = t;
}
return t;
default:
throw new IllegalArgumentException("type=" + type);
}
}
public Timer getSensorTimerLocked(int sensor, boolean create) {
Sensor se = mSensorStats.get(sensor);
if (se == null) {
if (!create) {
return null;
}
se = new Sensor(sensor);
mSensorStats.put(sensor, se);
}
Timer t = se.mTimer;
if (t != null) {
return t;
}
ArrayList<Timer> timers = mSensorTimers.get(sensor);
if (timers == null) {
timers = new ArrayList<Timer>();
mSensorTimers.put(sensor, timers);
}
t = new Timer(BatteryStats.SENSOR, timers, mUnpluggables);
se.mTimer = t;
return t;
}
public void noteStartWakeLocked(String name, int type) {
Timer t = getWakeTimerLocked(name, type);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
}
public void noteStopWakeLocked(String name, int type) {
Timer t = getWakeTimerLocked(name, type);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
}
public void noteStartSensor(int sensor) {
Timer t = getSensorTimerLocked(sensor, true);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
}
public void noteStopSensor(int sensor) {
// Don't create a timer if one doesn't already exist
Timer t = getSensorTimerLocked(sensor, false);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
}
public void noteStartGps() {
Timer t = getSensorTimerLocked(Sensor.GPS, true);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
}
public void noteStopGps() {
Timer t = getSensorTimerLocked(Sensor.GPS, false);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
}
public BatteryStatsImpl getBatteryStats() {
return BatteryStatsImpl.this;
}
}
public BatteryStatsImpl(String filename) {
mFile = new File(filename);
mBackupFile = new File(filename + ".bak");
mStartCount++;
mScreenOnTimer = new Timer(-1, null, mUnpluggables);
mPhoneOnTimer = new Timer(-2, null, mUnpluggables);
mWifiOnTimer = new Timer(-3, null, mUnpluggables);
mWifiRunningTimer = new Timer(-4, null, mUnpluggables);
mBluetoothOnTimer = new Timer(-5, null, mUnpluggables);
mOnBattery = mOnBatteryInternal = false;
mTrackBatteryPastUptime = 0;
mTrackBatteryPastRealtime = 0;
mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000;
mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
mUnpluggedStartLevel = 0;
mPluggedStartLevel = 0;
}
public BatteryStatsImpl(Parcel p) {
mFile = mBackupFile = null;
readFromParcel(p);
}
@Override
public int getStartCount() {
return mStartCount;
}
public boolean isOnBattery() {
return mOnBattery;
}
public void setOnBattery(boolean onBattery, int level) {
synchronized(this) {
if (mOnBattery != onBattery) {
mOnBattery = mOnBatteryInternal = onBattery;
long uptime = SystemClock.uptimeMillis() * 1000;
long mSecRealtime = SystemClock.elapsedRealtime();
long realtime = mSecRealtime * 1000;
if (onBattery) {
mTrackBatteryUptimeStart = uptime;
mTrackBatteryRealtimeStart = realtime;
mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
mUnpluggedStartLevel = level;
doUnplug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
} else {
mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
mPluggedStartLevel = level;
doPlug(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
}
if ((mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
writeLocked();
}
}
}
}
}
public long getAwakeTimeBattery() {
return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT);
}
public long getAwakeTimePlugged() {
return (SystemClock.uptimeMillis() * 1000) - getAwakeTimeBattery();
}
@Override
public long computeUptime(long curTime, int which) {
switch (which) {
case STATS_TOTAL: return mUptime + (curTime-mUptimeStart);
case STATS_LAST: return mLastUptime;
case STATS_CURRENT: return (curTime-mUptimeStart);
case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
}
return 0;
}
@Override
public long computeRealtime(long curTime, int which) {
switch (which) {
case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart);
case STATS_LAST: return mLastRealtime;
case STATS_CURRENT: return (curTime-mRealtimeStart);
case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
}
return 0;
}
@Override
public long computeBatteryUptime(long curTime, int which) {
switch (which) {
case STATS_TOTAL:
return mBatteryUptime + getBatteryUptime(curTime);
case STATS_LAST:
return mBatteryLastUptime;
case STATS_CURRENT:
return getBatteryUptime(curTime);
case STATS_UNPLUGGED:
return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime;
}
return 0;
}
@Override
public long computeBatteryRealtime(long curTime, int which) {
switch (which) {
case STATS_TOTAL:
return mBatteryRealtime + getBatteryRealtimeLocked(curTime);
case STATS_LAST:
return mBatteryLastRealtime;
case STATS_CURRENT:
return getBatteryRealtimeLocked(curTime);
case STATS_UNPLUGGED:
return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime;
}
return 0;
}
long getBatteryUptimeLocked(long curTime) {
long time = mTrackBatteryPastUptime;
if (mOnBatteryInternal) {
time += curTime - mTrackBatteryUptimeStart;
}
return time;
}
long getBatteryUptimeLocked() {
return getBatteryUptime(SystemClock.uptimeMillis() * 1000);
}
@Override
public long getBatteryUptime(long curTime) {
return getBatteryUptimeLocked(curTime);
}
long getBatteryRealtimeLocked(long curTime) {
long time = mTrackBatteryPastRealtime;
if (mOnBatteryInternal) {
time += curTime - mTrackBatteryRealtimeStart;
}
return time;
}
@Override
public long getBatteryRealtime(long curTime) {
return getBatteryRealtimeLocked(curTime);
}
@Override
public int getUnpluggedStartLevel() {
synchronized(this) {
return getUnluggedStartLevelLocked();
}
}
public int getUnluggedStartLevelLocked() {
return mUnpluggedStartLevel;
}
@Override
public int getPluggedStartLevel() {
synchronized(this) {
return getPluggedStartLevelLocked();
}
}
public int getPluggedStartLevelLocked() {
return mPluggedStartLevel;
}
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
public Uid getUidStatsLocked(int uid) {
Uid u = mUidStats.get(uid);
if (u == null) {
u = new Uid(uid);
mUidStats.put(uid, u);
}
return u;
}
/**
* Remove the statistics object for a particular uid.
*/
public void removeUidStatsLocked(int uid) {
mUidStats.remove(uid);
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
public Uid.Proc getProcessStatsLocked(int uid, String name) {
Uid u = getUidStatsLocked(uid);
return u.getProcessStatsLocked(name);
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
public Uid.Pkg getPackageStatsLocked(int uid, String pkg) {
Uid u = getUidStatsLocked(uid);
return u.getPackageStatsLocked(pkg);
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
Uid u = getUidStatsLocked(uid);
return u.getServiceStatsLocked(pkg, name);
}
public void writeLocked() {
if ((mFile == null) || (mBackupFile == null)) {
Log.w("BatteryStats", "writeLocked: no file associated with this instance");
return;
}
// Keep the old file around until we know the new one has
// been successfully written.
if (mFile.exists()) {
if (mBackupFile.exists()) {
mBackupFile.delete();
}
mFile.renameTo(mBackupFile);
}
try {
FileOutputStream stream = new FileOutputStream(mFile);
Parcel out = Parcel.obtain();
writeSummaryToParcel(out);
stream.write(out.marshall());
out.recycle();
stream.flush();
stream.close();
mBackupFile.delete();
mLastWriteTime = SystemClock.elapsedRealtime();
} catch (IOException e) {
Log.e("BatteryStats", "Error writing battery statistics", e);
}
}
static byte[] readFully(FileInputStream stream) throws java.io.IOException {
int pos = 0;
int avail = stream.available();
byte[] data = new byte[avail];
while (true) {
int amt = stream.read(data, pos, data.length-pos);
//Log.i("foo", "Read " + amt + " bytes at " + pos
// + " of avail " + data.length);
if (amt <= 0) {
//Log.i("foo", "**** FINISHED READING: pos=" + pos
// + " len=" + data.length);
return data;
}
pos += amt;
avail = stream.available();
if (avail > data.length-pos) {
byte[] newData = new byte[pos+avail];
System.arraycopy(data, 0, newData, 0, pos);
data = newData;
}
}
}
public void readLocked() {
if ((mFile == null) || (mBackupFile == null)) {
Log.w("BatteryStats", "readLocked: no file associated with this instance");
return;
}
mUidStats.clear();
FileInputStream stream = null;
if (mBackupFile.exists()) {
try {
stream = new FileInputStream(mBackupFile);
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
}
try {
if (stream == null) {
if (!mFile.exists()) {
return;
}
stream = new FileInputStream(mFile);
}
byte[] raw = readFully(stream);
Parcel in = Parcel.obtain();
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
stream.close();
readSummaryFromParcel(in);
} catch(java.io.IOException e) {
Log.e("BatteryStats", "Error reading battery statistics", e);
}
}
public int describeContents() {
return 0;
}
private void readSummaryFromParcel(Parcel in) {
final int version = in.readInt();
if (version != VERSION) {
Log.w("BatteryStats", "readFromParcel: version got " + version
+ ", expected " + VERSION + "; erasing old stats");
return;
}
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
mBatteryLastUptime = in.readLong();
mBatteryRealtime = in.readLong();
mBatteryLastRealtime = in.readLong();
mUptime = in.readLong();
mLastUptime = in.readLong();
mRealtime = in.readLong();
mLastRealtime = in.readLong();
mUnpluggedStartLevel = in.readInt();
mPluggedStartLevel = in.readInt();
mStartCount++;
mScreenOn = false;
mScreenOnTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
mPhoneOnTimer.readSummaryFromParcelLocked(in);
mWifiOn = false;
mWifiOnTimer.readSummaryFromParcelLocked(in);
mWifiRunning = false;
mWifiRunningTimer.readSummaryFromParcelLocked(in);
mBluetoothOn = false;
mBluetoothOnTimer.readSummaryFromParcelLocked(in);
final int NU = in.readInt();
for (int iu = 0; iu < NU; iu++) {
int uid = in.readInt();
Uid u = new Uid(uid);
mUidStats.put(uid, u);
u.mFullWifiLockOut = false;
u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
u.mScanWifiLockOut = false;
u.mScanWifiLockTimer.readSummaryFromParcelLocked(in);
int NW = in.readInt();
for (int iw = 0; iw < NW; iw++) {
String wlName = in.readString();
if (in.readInt() != 0) {
u.getWakeTimerLocked(wlName, WAKE_TYPE_FULL).readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.getWakeTimerLocked(wlName, WAKE_TYPE_PARTIAL).readSummaryFromParcelLocked(in);
}
if (in.readInt() != 0) {
u.getWakeTimerLocked(wlName, WAKE_TYPE_WINDOW).readSummaryFromParcelLocked(in);
}
}
int NP = in.readInt();
for (int is = 0; is < NP; is++) {
int seNumber = in.readInt();
if (in.readInt() != 0) {
u.getSensorTimerLocked(seNumber, true)
.readSummaryFromParcelLocked(in);
}
}
NP = in.readInt();
for (int ip = 0; ip < NP; ip++) {
String procName = in.readString();
Uid.Proc p = u.getProcessStatsLocked(procName);
p.mUserTime = p.mLoadedUserTime = in.readLong();
p.mLastUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
p.mLastSystemTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
p.mLastStarts = in.readInt();
}
NP = in.readInt();
for (int ip = 0; ip < NP; ip++) {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
p.mWakeups = p.mLoadedWakeups = in.readInt();
p.mLastWakeups = in.readInt();
final int NS = in.readInt();
for (int is = 0; is < NS; is++) {
String servName = in.readString();
Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName);
s.mStartTime = s.mLoadedStartTime = in.readLong();
s.mLastStartTime = in.readLong();
s.mStarts = s.mLoadedStarts = in.readInt();
s.mLastStarts = in.readInt();
s.mLaunches = s.mLoadedLaunches = in.readInt();
s.mLastLaunches = in.readInt();
}
}
u.mLoadedTcpBytesReceived = in.readLong();
u.mLoadedTcpBytesSent = in.readLong();
}
}
/**
* Writes a summary of the statistics to a Parcel, in a format suitable to be written to
* disk. This format does not allow a lossless round-trip.
*
* @param out the Parcel to be written to.
*/
public void writeSummaryToParcel(Parcel out) {
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
final long NOW = getBatteryUptimeLocked(NOW_SYS);
final long NOWREAL = getBatteryRealtimeLocked(NOWREAL_SYS);
out.writeInt(VERSION);
out.writeInt(mStartCount);
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_TOTAL));
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_TOTAL));
out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_CURRENT));
out.writeLong(computeUptime(NOW_SYS, STATS_TOTAL));
out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT));
out.writeInt(mUnpluggedStartLevel);
out.writeInt(mPluggedStartLevel);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
final int NU = mUidStats.size();
out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) {
out.writeInt(mUidStats.keyAt(iu));
Uid u = mUidStats.valueAt(iu);
u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
int NW = u.mWakelockStats.size();
out.writeInt(NW);
if (NW > 0) {
for (Map.Entry<String, BatteryStatsImpl.Uid.Wakelock> ent
: u.mWakelockStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Wakelock wl = ent.getValue();
if (wl.mTimerFull != null) {
out.writeInt(1);
wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL);
} else {
out.writeInt(0);
}
if (wl.mTimerPartial != null) {
out.writeInt(1);
wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL);
} else {
out.writeInt(0);
}
if (wl.mTimerWindow != null) {
out.writeInt(1);
wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL);
} else {
out.writeInt(0);
}
}
}
int NSE = u.mSensorStats.size();
out.writeInt(NSE);
if (NSE > 0) {
for (Map.Entry<Integer, BatteryStatsImpl.Uid.Sensor> ent
: u.mSensorStats.entrySet()) {
out.writeInt(ent.getKey());
Uid.Sensor se = ent.getValue();
if (se.mTimer != null) {
out.writeInt(1);
se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL);
} else {
out.writeInt(0);
}
}
}
int NP = u.mProcessStats.size();
out.writeInt(NP);
if (NP > 0) {
for (Map.Entry<String, BatteryStatsImpl.Uid.Proc> ent
: u.mProcessStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Proc ps = ent.getValue();
out.writeLong(ps.mUserTime);
out.writeLong(ps.mUserTime - ps.mLoadedUserTime);
out.writeLong(ps.mSystemTime);
out.writeLong(ps.mSystemTime - ps.mLoadedSystemTime);
out.writeInt(ps.mStarts);
out.writeInt(ps.mStarts - ps.mLoadedStarts);
}
}
NP = u.mPackageStats.size();
out.writeInt(NP);
if (NP > 0) {
for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg> ent
: u.mPackageStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Pkg ps = ent.getValue();
out.writeInt(ps.mWakeups);
out.writeInt(ps.mWakeups - ps.mLoadedWakeups);
final int NS = ps.mServiceStats.size();
out.writeInt(NS);
if (NS > 0) {
for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg.Serv> sent
: ps.mServiceStats.entrySet()) {
out.writeString(sent.getKey());
BatteryStatsImpl.Uid.Pkg.Serv ss = sent.getValue();
long time = ss.getStartTimeToNowLocked(NOW);
out.writeLong(time);
out.writeLong(time - ss.mLoadedStartTime);
out.writeInt(ss.mStarts);
out.writeInt(ss.mStarts - ss.mLoadedStarts);
out.writeInt(ss.mLaunches);
out.writeInt(ss.mLaunches - ss.mLoadedLaunches);
}
}
}
}
out.writeLong(u.getTcpBytesReceived(STATS_TOTAL));
out.writeLong(u.getTcpBytesSent(STATS_TOTAL));
}
}
public void readFromParcel(Parcel in) {
readFromParcelLocked(in);
}
void readFromParcelLocked(Parcel in) {
int magic = in.readInt();
if (magic != MAGIC) {
throw new ParcelFormatException("Bad magic number");
}
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
mBatteryLastUptime = in.readLong();
mBatteryRealtime = in.readLong();
mBatteryLastRealtime = in.readLong();
mScreenOn = false;
mScreenOnTimer = new Timer(-1, null, mUnpluggables, in);
mPhoneOn = false;
mPhoneOnTimer = new Timer(-2, null, mUnpluggables, in);
mWifiOn = false;
mWifiOnTimer = new Timer(-2, null, mUnpluggables, in);
mWifiRunning = false;
mWifiRunningTimer = new Timer(-2, null, mUnpluggables, in);
mBluetoothOn = false;
mBluetoothOnTimer = new Timer(-2, null, mUnpluggables, in);
mUptime = in.readLong();
mUptimeStart = in.readLong();
mLastUptime = in.readLong();
mRealtime = in.readLong();
mRealtimeStart = in.readLong();
mLastRealtime = in.readLong();
mOnBattery = in.readInt() != 0;
mOnBatteryInternal = false; // we are no longer really running.
mTrackBatteryPastUptime = in.readLong();
mTrackBatteryUptimeStart = in.readLong();
mTrackBatteryPastRealtime = in.readLong();
mTrackBatteryRealtimeStart = in.readLong();
mUnpluggedBatteryUptime = in.readLong();
mUnpluggedBatteryRealtime = in.readLong();
mUnpluggedStartLevel = in.readInt();
mPluggedStartLevel = in.readInt();
mLastWriteTime = in.readLong();
mPartialTimers.clear();
mFullTimers.clear();
mWindowTimers.clear();
int numUids = in.readInt();
mUidStats.clear();
for (int i = 0; i < numUids; i++) {
int uid = in.readInt();
Uid u = new Uid(uid);
u.readFromParcelLocked(mUnpluggables, in);
mUidStats.append(uid, u);
}
}
public void writeToParcel(Parcel out, int flags) {
writeToParcelLocked(out, flags);
}
@SuppressWarnings("unused")
void writeToParcelLocked(Parcel out, int flags) {
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
out.writeInt(MAGIC);
out.writeInt(mStartCount);
out.writeLong(mBatteryUptime);
out.writeLong(mBatteryLastUptime);
out.writeLong(mBatteryRealtime);
out.writeLong(mBatteryLastRealtime);
mScreenOnTimer.writeToParcel(out, batteryRealtime);
mPhoneOnTimer.writeToParcel(out, batteryRealtime);
mWifiOnTimer.writeToParcel(out, batteryRealtime);
mWifiRunningTimer.writeToParcel(out, batteryRealtime);
mBluetoothOnTimer.writeToParcel(out, batteryRealtime);
out.writeLong(mUptime);
out.writeLong(mUptimeStart);
out.writeLong(mLastUptime);
out.writeLong(mRealtime);
out.writeLong(mRealtimeStart);
out.writeLong(mLastRealtime);
out.writeInt(mOnBattery ? 1 : 0);
out.writeLong(batteryUptime);
out.writeLong(mTrackBatteryUptimeStart);
out.writeLong(batteryRealtime);
out.writeLong(mTrackBatteryRealtimeStart);
out.writeLong(mUnpluggedBatteryUptime);
out.writeLong(mUnpluggedBatteryRealtime);
out.writeInt(mUnpluggedStartLevel);
out.writeInt(mPluggedStartLevel);
out.writeLong(mLastWriteTime);
int size = mUidStats.size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
out.writeInt(mUidStats.keyAt(i));
Uid uid = mUidStats.valueAt(i);
uid.writeToParcelLocked(out, batteryRealtime);
}
}
public static final Parcelable.Creator<BatteryStatsImpl> CREATOR =
new Parcelable.Creator<BatteryStatsImpl>() {
public BatteryStatsImpl createFromParcel(Parcel in) {
return new BatteryStatsImpl(in);
}
public BatteryStatsImpl[] newArray(int size) {
return new BatteryStatsImpl[size];
}
};
public void dumpLocked(Printer pw) {
if (DEBUG) {
Log.i(TAG, "*** Screen timer:");
mScreenOnTimer.logState();
Log.i(TAG, "*** Phone timer:");
mPhoneOnTimer.logState();
Log.i(TAG, "*** Wifi timer:");
mWifiOnTimer.logState();
Log.i(TAG, "*** WifiRunning timer:");
mWifiRunningTimer.logState();
Log.i(TAG, "*** Bluetooth timer:");
mBluetoothOnTimer.logState();
}
super.dumpLocked(pw);
}
}