blob: ea31dc0161af2a886cddf37b288f57e207fda9be [file] [log] [blame]
/*
* Copyright (C) 2006 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.server.am;
import com.android.internal.app.IBatteryStats;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Config;
import android.util.Log;
import android.util.SparseArray;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* All information we are collecting about things that can happen that impact
* battery life.
*/
public final class BatteryStats extends IBatteryStats.Stub {
public static final int WAKE_TYPE_PARTIAL = 0;
public static final int WAKE_TYPE_FULL = 1;
public static final int WAKE_TYPE_WINDOW = 2;
/**
* Include all of the loaded data in the stats.
*/
public static final int STATS_LOADED = 0;
/**
* Include only the last run in the stats.
*/
public static final int STATS_LAST = 1;
/**
* Include only the current run in the stats.
*/
public static final int STATS_CURRENT = 2;
static final int VERSION = 11;
static IBatteryStats sService;
final File mFile;
final File mBackupFile;
Context mContext;
/**
* The statistics we have collected organized by uids.
*/
final SparseArray<Uid> uidStats = new SparseArray<Uid>();
int mStartCount;
long mBatteryUptime;
long mBatteryUptimeStart;
long mBatteryLastUptime;
long mBatteryRealtime;
long mBatteryRealtimeStart;
long mBatteryLastRealtime;
long mUptime;
long mUptimeStart;
long mLastUptime;
long mRealtime;
long mRealtimeStart;
long mLastRealtime;
/**
* These provide time bases that discount the time the device is plugged
* in to power.
*/
boolean mOnBattery = true;
long mTrackBatteryPastUptime = 0;
long mTrackBatteryUptimeStart = 0;
long mTrackBatteryPastRealtime = 0;
long mTrackBatteryRealtimeStart = 0;
/**
* State for keeping track of timing information.
*/
final static class Timer {
long totalTime;
long startTime;
int nesting;
int count;
long loadedTotalTime;
int loadedCount;
long lastTotalTime;
int lastCount;
void startRunningLocked(BatteryStats stats) {
if (nesting == 0) {
nesting = 1;
startTime = stats.getBatteryUptimeLocked();
count++;
} else {
nesting++;
}
}
void stopRunningLocked(BatteryStats stats) {
if (nesting == 1) {
nesting = 0;
long heldTime = stats.getBatteryUptimeLocked() - startTime;
if (heldTime != 0) {
totalTime += heldTime;
} else {
count--;
}
} else {
nesting--;
}
}
long computeRunTimeLocked(long curTime) {
return totalTime + (nesting > 0 ? (curTime-startTime) : 0);
}
void writeLocked(Parcel out, long curTime) throws java.io.IOException {
long runTime = computeRunTimeLocked(curTime);
out.writeLong(runTime);
out.writeLong(runTime - loadedTotalTime);
out.writeInt(count);
out.writeInt(count - loadedCount);
}
void readLocked(Parcel in) throws java.io.IOException {
totalTime = loadedTotalTime = in.readLong();
lastTotalTime = in.readLong();
count = loadedCount = in.readInt();
lastCount = in.readInt();
nesting = 0;
}
}
/**
* The statistics associated with a particular uid.
*/
final class Uid {
/**
* The statics we have collected for this uid's wake locks.
*/
final HashMap<String, Wakelock> wakelockStats = new HashMap<String, Wakelock>();
/**
* The statics we have collected for this uid's processes.
*/
final HashMap<String, Proc> processStats = new HashMap<String, Proc>();
/**
* The statics we have collected for this uid's processes.
*/
final HashMap<String, Pkg> packageStats = new HashMap<String, Pkg>();
/**
* The statistics associated with a particular wake lock.
*/
final class Wakelock {
/**
* How long (in ms) this uid has been keeping the device partially awake.
*/
Timer wakeTimePartial;
/**
* How long (in ms) this uid has been keeping the device fully awake.
*/
Timer wakeTimeFull;
/**
* How long (in ms) this uid has had a window keeping the device awake.
*/
Timer wakeTimeWindow;
}
/**
* The statistics associated with a particular process.
*/
final class Proc {
/**
* Total time (in 1/100 sec) spent executing in user code.
*/
long userTime;
/**
* Total time (in 1/100 sec) spent executing in kernel code.
*/
long systemTime;
/**
* Number of times the process has been started.
*/
int starts;
/**
* The amount of user time loaded from a previous save.
*/
long loadedUserTime;
/**
* The amount of system time loaded from a previous save.
*/
long loadedSystemTime;
/**
* The number of times the process has started from a previous save.
*/
int loadedStarts;
/**
* The amount of user time loaded from the previous run.
*/
long lastUserTime;
/**
* The amount of system time loaded from the previous run.
*/
long lastSystemTime;
/**
* The number of times the process has started from the previous run.
*/
int lastStarts;
BatteryStats getBatteryStats() {
return BatteryStats.this;
}
}
/**
* The statistics associated with a particular package.
*/
final class Pkg {
/**
* Number of times this package has done something that could wake up the
* device from sleep.
*/
int wakeups;
/**
* Number of things that could wake up the device loaded from a
* previous save.
*/
int loadedWakeups;
/**
* Number of things that could wake up the device as of the
* last run.
*/
int lastWakeups;
/**
* The statics we have collected for this package's services.
*/
final HashMap<String, Serv> serviceStats = new HashMap<String, Serv>();
/**
* The statistics associated with a particular service.
*/
final class Serv {
/**
* Total time (ms) the service has been left started.
*/
long startTime;
/**
* If service has been started and not yet stopped, this is
* when it was started.
*/
long runningSince;
/**
* True if we are currently running.
*/
boolean running;
/**
* Total number of times startService() has been called.
*/
int starts;
/**
* Total time (ms) the service has been left launched.
*/
long launchedTime;
/**
* If service has been launched and not yet exited, this is
* when it was launched.
*/
long launchedSince;
/**
* True if we are currently launched.
*/
boolean launched;
/**
* Total number times the service has been launched.
*/
int launches;
/**
* The amount of time spent started loaded from a previous save.
*/
long loadedStartTime;
/**
* The number of starts loaded from a previous save.
*/
int loadedStarts;
/**
* The number of launches loaded from a previous save.
*/
int loadedLaunches;
/**
* The amount of time spent started as of the last run.
*/
long lastStartTime;
/**
* The number of starts as of the last run.
*/
int lastStarts;
/**
* The number of launches as of the last run.
*/
int lastLaunches;
long getLaunchTimeToNowLocked(long now) {
if (!launched) return launchedTime;
return launchedTime + now - launchedSince;
}
long getStartTimeToNowLocked(long now) {
if (!running) return startTime;
return startTime + now - runningSince;
}
void startLaunchedLocked() {
if (!launched) {
launches++;
launchedSince = getBatteryUptimeLocked();
launched = true;
}
}
void stopLaunchedLocked() {
if (launched) {
long time = getBatteryUptimeLocked() - launchedSince;
if (time > 0) {
launchedTime += time;
} else {
launches--;
}
launched = false;
}
}
void startRunningLocked() {
if (!running) {
starts++;
runningSince = getBatteryUptimeLocked();
running = true;
}
}
void stopRunningLocked() {
if (running) {
long time = getBatteryUptimeLocked() - runningSince;
if (time > 0) {
startTime += time;
} else {
starts--;
}
running = false;
}
}
BatteryStats getBatteryStats() {
return BatteryStats.this;
}
}
BatteryStats getBatteryStats() {
return BatteryStats.this;
}
private final Serv newServiceStatsLocked() {
return new Serv();
}
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
Proc getProcessStatsLocked(String name) {
Proc ps = processStats.get(name);
if (ps == null) {
ps = new Proc();
processStats.put(name, ps);
}
return ps;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
Pkg getPackageStatsLocked(String name) {
Pkg ps = packageStats.get(name);
if (ps == null) {
ps = new Pkg();
packageStats.put(name, ps);
}
return ps;
}
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
*/
Pkg.Serv getServiceStatsLocked(String pkg, String serv) {
Pkg ps = getPackageStatsLocked(pkg);
Pkg.Serv ss = ps.serviceStats.get(serv);
if (ss == null) {
ss = ps.newServiceStatsLocked();
ps.serviceStats.put(serv, ss);
}
return ss;
}
Timer getWakeTimerLocked(String name, int type) {
Wakelock wl = wakelockStats.get(name);
if (wl == null) {
wl = new Wakelock();
wakelockStats.put(name, wl);
}
Timer t = null;
switch (type) {
case WAKE_TYPE_PARTIAL:
t = wl.wakeTimePartial;
if (t == null) {
t = new Timer();
wl.wakeTimePartial = t;
}
return t;
case WAKE_TYPE_FULL:
t = wl.wakeTimeFull;
if (t == null) {
t = new Timer();
wl.wakeTimeFull = t;
}
return t;
case WAKE_TYPE_WINDOW:
t = wl.wakeTimeWindow;
if (t == null) {
t = new Timer();
wl.wakeTimeWindow = t;
}
return t;
}
return t;
}
void noteStartWakeLocked(String name, int type) {
Timer t = getWakeTimerLocked(name, type);
if (t != null) {
t.startRunningLocked(BatteryStats.this);
}
}
void noteStopWakeLocked(String name, int type) {
Timer t = getWakeTimerLocked(name, type);
if (t != null) {
t.stopRunningLocked(BatteryStats.this);
}
}
BatteryStats getBatteryStats() {
return BatteryStats.this;
}
}
BatteryStats(String filename) {
mFile = new File(filename);
mBackupFile = new File(filename + ".bak");
mStartCount++;
mUptimeStart = SystemClock.uptimeMillis();
mRealtimeStart = SystemClock.elapsedRealtime();
}
public void publish(Context context) {
mContext = context;
ServiceManager.addService("batteryinfo", asBinder());
}
public static IBatteryStats getService() {
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService("batteryinfo");
sService = asInterface(b);
return sService;
}
public void noteStartWakelock(int uid, String name, int type) {
enforceCallingPermission();
synchronized(this) {
getUidStatsLocked(uid).noteStartWakeLocked(name, type);
}
}
public void noteStopWakelock(int uid, String name, int type) {
enforceCallingPermission();
synchronized(this) {
getUidStatsLocked(uid).noteStopWakeLocked(name, type);
}
}
public boolean isOnBattery() {
return mOnBattery;
}
public void setOnBattery(boolean onBattery) {
enforceCallingPermission();
synchronized(this) {
if (mOnBattery != onBattery) {
if (onBattery) {
mTrackBatteryUptimeStart = SystemClock.uptimeMillis();
mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime();
} else {
mTrackBatteryPastUptime +=
SystemClock.uptimeMillis() - mTrackBatteryUptimeStart;
mTrackBatteryPastRealtime +=
SystemClock.elapsedRealtime() - mTrackBatteryRealtimeStart;
}
mOnBattery = onBattery;
}
}
}
public long getAwakeTimeBattery() {
return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT);
}
public long getAwakeTimePlugged() {
return SystemClock.uptimeMillis() - getAwakeTimeBattery();
}
long getBatteryUptimeLocked() {
long time = mTrackBatteryPastUptime;
if (mOnBattery) {
time += SystemClock.uptimeMillis() - mTrackBatteryUptimeStart;
}
return time;
}
long getBatteryRealtimeLocked() {
long time = mTrackBatteryPastRealtime;
if (mOnBattery) {
time += SystemClock.elapsedRealtime() - mTrackBatteryRealtimeStart;
}
return time;
}
long computeUptime(long curTime, int which) {
switch (which) {
case STATS_LOADED: return mUptime + (curTime-mUptimeStart);
case STATS_LAST: return mLastUptime;
case STATS_CURRENT: return (curTime-mUptimeStart);
}
return 0;
}
long computeRealtime(long curTime, int which) {
switch (which) {
case STATS_LOADED: return mRealtime + (curTime-mRealtimeStart);
case STATS_LAST: return mLastRealtime;
case STATS_CURRENT: return (curTime-mRealtimeStart);
}
return 0;
}
long computeBatteryUptime(long curTime, int which) {
switch (which) {
case STATS_LOADED: return mBatteryUptime + (curTime-mBatteryUptimeStart);
case STATS_LAST: return mBatteryLastUptime;
case STATS_CURRENT: return (curTime-mBatteryUptimeStart);
}
return 0;
}
long computeBatteryRealtime(long curTime, int which) {
switch (which) {
case STATS_LOADED: return mBatteryRealtime + (curTime-mBatteryRealtimeStart);
case STATS_LAST: return mBatteryLastRealtime;
case STATS_CURRENT: return (curTime-mBatteryRealtimeStart);
}
return 0;
}
public void enforceCallingPermission() {
if (Binder.getCallingPid() == Process.myPid()) {
return;
}
mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
Uid getUidStatsLocked(int uid) {
Uid u = uidStats.get(uid);
if (u == null) {
u = new Uid();
uidStats.put(uid, u);
}
return u;
}
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
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.
*/
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.
*/
Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
Uid u = getUidStatsLocked(uid);
return u.getServiceStatsLocked(pkg, name);
}
private final static String formatTime(long time) {
long sec = time/100;
StringBuilder sb = new StringBuilder();
sb.append(sec);
if (time != 0) {
sb.append('.');
sb.append((char)(((time/10)%10)+'0'));
sb.append((char)((time%10)+'0'));
}
sb.append(" sec");
return sb.toString();
}
private final static String formatTimeMs(long time) {
long sec = time/1000;
StringBuilder sb = new StringBuilder();
sb.append(sec);
if (time != 0) {
sb.append('.');
sb.append((char)(((time/100)%10)+'0'));
sb.append((char)(((time/10)%10)+'0'));
sb.append((char)((time%10)+'0'));
}
sb.append(" sec");
return sb.toString();
}
final String printWakeLock(StringBuilder sb, Timer timer, long now,
String name, int which, String linePrefix) {
if (timer != null) {
long totalTime;
int count;
if (which == STATS_LAST) {
totalTime = timer.lastTotalTime;
count = timer.lastCount;
} else {
totalTime = timer.computeRunTimeLocked(now);
count = timer.count;
if (which == STATS_CURRENT) {
totalTime -= timer.loadedTotalTime;
count -= timer.loadedCount;
}
}
if (totalTime != 0) {
sb.append(linePrefix);
sb.append(formatTimeMs(totalTime));
sb.append(' ');
sb.append(name);
sb.append(' ');
sb.append('(');
sb.append(count);
sb.append(" times)");
return ", ";
}
}
return linePrefix;
}
final void dumpLocked(FileDescriptor fd, PrintWriter pw, String prefix,
int which) {
final long NOW = getBatteryUptimeLocked();
StringBuilder sb = new StringBuilder();
if (which == STATS_LOADED) {
pw.println(prefix + "Current and Historic Battery Usage Statistics:");
pw.println(prefix + " System starts: " + mStartCount);
} else if (which == STATS_LAST) {
pw.println(prefix + "Last Battery Usage Statistics:");
} else {
pw.println(prefix + "Current Battery Usage Statistics:");
}
pw.println(prefix
+ " On battery: "
+ formatTimeMs(computeBatteryUptime(NOW, which))
+ " uptime, "
+ formatTimeMs(computeBatteryRealtime(getBatteryRealtimeLocked(),
which))
+ " realtime");
pw.println(prefix
+ " Total: "
+ formatTimeMs(computeUptime(SystemClock.uptimeMillis(), which))
+ " uptime, "
+ formatTimeMs(computeRealtime(SystemClock.elapsedRealtime(),
which))
+ " realtime");
pw.println(" ");
final int NU = uidStats.size();
for (int iu=0; iu<NU; iu++) {
final int uid = uidStats.keyAt(iu);
Uid u = uidStats.valueAt(iu);
pw.println(prefix + " #" + uid + ":");
boolean uidActivity = false;
if (u.wakelockStats.size() > 0) {
for (Map.Entry<String, BatteryStats.Uid.Wakelock> ent
: u.wakelockStats.entrySet()) {
Uid.Wakelock wl = ent.getValue();
String linePrefix = ": ";
sb.setLength(0);
sb.append(prefix);
sb.append(" Wake lock ");
sb.append(ent.getKey());
linePrefix = printWakeLock(sb, wl.wakeTimeFull, NOW,
"full", which, linePrefix);
linePrefix = printWakeLock(sb, wl.wakeTimePartial, NOW,
"partial", which, linePrefix);
linePrefix = printWakeLock(sb, wl.wakeTimeWindow, NOW,
"window", which, linePrefix);
if (linePrefix.equals(": ")) {
sb.append(": (nothing executed)");
}
pw.println(sb.toString());
uidActivity = true;
}
}
if (u.processStats.size() > 0) {
for (Map.Entry<String, BatteryStats.Uid.Proc> ent
: u.processStats.entrySet()) {
BatteryStats.Uid.Proc ps = ent.getValue();
long userTime;
long systemTime;
int starts;
if (which == STATS_LAST) {
userTime = ps.lastUserTime;
systemTime = ps.lastSystemTime;
starts = ps.lastStarts;
} else {
userTime = ps.userTime;
systemTime = ps.systemTime;
starts = ps.starts;
if (which == STATS_CURRENT) {
userTime -= ps.loadedUserTime;
systemTime -= ps.loadedSystemTime;
starts -= ps.loadedStarts;
}
}
if (userTime != 0 || systemTime != 0 || starts != 0) {
pw.println(prefix + " Proc " + ent.getKey() + ":");
pw.println(prefix + " CPU: " + formatTime(userTime) + " user + "
+ formatTime(systemTime) + " kernel");
pw.println(prefix + " " + starts + " process starts");
uidActivity = true;
}
}
}
if (u.packageStats.size() > 0) {
for (Map.Entry<String, BatteryStats.Uid.Pkg> ent
: u.packageStats.entrySet()) {
pw.println(prefix + " Apk " + ent.getKey() + ":");
boolean apkActivity = false;
BatteryStats.Uid.Pkg ps = ent.getValue();
int wakeups;
if (which == STATS_LAST) {
wakeups = ps.lastWakeups;
} else {
wakeups = ps.wakeups;
if (which == STATS_CURRENT) {
wakeups -= ps.loadedWakeups;
}
}
if (wakeups != 0) {
pw.println(prefix + " " + wakeups + " wakeup alarms");
apkActivity = true;
}
if (ps.serviceStats.size() > 0) {
for (Map.Entry<String, BatteryStats.Uid.Pkg.Serv> sent
: ps.serviceStats.entrySet()) {
BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
long time;
int starts;
int launches;
if (which == STATS_LAST) {
time = ss.lastStartTime;
starts = ss.lastStarts;
launches = ss.lastLaunches;
} else {
time = ss.getStartTimeToNowLocked(NOW);
starts = ss.starts;
launches = ss.launches;
if (which == STATS_CURRENT) {
time -= ss.loadedStartTime;
starts -= ss.loadedStarts;
launches -= ss.loadedLaunches;
}
}
if (time != 0 || starts != 0 || launches != 0) {
pw.println(prefix + " Service " + sent.getKey() + ":");
pw.println(prefix + " Time spent started: "
+ formatTimeMs(time));
pw.println(prefix + " Starts: " + starts
+ ", launches: " + launches);
apkActivity = true;
}
}
}
if (!apkActivity) {
pw.println(prefix + " (nothing executed)");
}
uidActivity = true;
}
}
if (!uidActivity) {
pw.println(prefix + " (nothing executed)");
}
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (this) {
dumpLocked(fd, pw, "", STATS_LOADED);
pw.println("");
dumpLocked(fd, pw, "", STATS_LAST);
pw.println("");
dumpLocked(fd, pw, "", STATS_CURRENT);
}
}
void writeLocked() {
// 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);
final long NOW = getBatteryUptimeLocked();
final long NOWREAL = getBatteryRealtimeLocked();
final long NOW_SYS = SystemClock.uptimeMillis();
final long NOWREAL_SYS = SystemClock.elapsedRealtime();
Parcel out = Parcel.obtain();
out.writeInt(VERSION);
out.writeInt(mStartCount);
out.writeLong(computeBatteryUptime(NOW, STATS_LOADED));
out.writeLong(computeBatteryUptime(NOW, STATS_CURRENT));
out.writeLong(computeBatteryRealtime(NOWREAL, STATS_LOADED));
out.writeLong(computeBatteryRealtime(NOWREAL, STATS_CURRENT));
out.writeLong(computeUptime(NOW_SYS, STATS_LOADED));
out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_LOADED));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT));
final int NU = uidStats.size();
out.writeInt(NU);
for (int iu=0; iu<NU; iu++) {
out.writeInt(uidStats.keyAt(iu));
Uid u = uidStats.valueAt(iu);
int NW = u.wakelockStats.size();
out.writeInt(NW);
if (NW > 0) {
for (Map.Entry<String, BatteryStats.Uid.Wakelock> ent
: u.wakelockStats.entrySet()) {
out.writeString(ent.getKey());
Uid.Wakelock wl = ent.getValue();
if (wl.wakeTimeFull != null) {
out.writeInt(1);
wl.wakeTimeFull.writeLocked(out, NOW);
} else {
out.writeInt(0);
}
if (wl.wakeTimePartial != null) {
out.writeInt(1);
wl.wakeTimePartial.writeLocked(out, NOW);
} else {
out.writeInt(0);
}
if (wl.wakeTimeWindow != null) {
out.writeInt(1);
wl.wakeTimeWindow.writeLocked(out, NOW);
} else {
out.writeInt(0);
}
}
}
int NP = u.processStats.size();
out.writeInt(NP);
if (NP > 0) {
for (Map.Entry<String, BatteryStats.Uid.Proc> ent
: u.processStats.entrySet()) {
out.writeString(ent.getKey());
BatteryStats.Uid.Proc ps = ent.getValue();
out.writeLong(ps.userTime);
out.writeLong(ps.userTime - ps.loadedUserTime);
out.writeLong(ps.systemTime);
out.writeLong(ps.systemTime - ps.loadedSystemTime);
out.writeInt(ps.starts);
out.writeInt(ps.starts - ps.loadedStarts);
}
}
NP = u.packageStats.size();
out.writeInt(NP);
if (NP > 0) {
for (Map.Entry<String, BatteryStats.Uid.Pkg> ent
: u.packageStats.entrySet()) {
out.writeString(ent.getKey());
BatteryStats.Uid.Pkg ps = ent.getValue();
out.writeInt(ps.wakeups);
out.writeInt(ps.wakeups - ps.loadedWakeups);
final int NS = ps.serviceStats.size();
out.writeInt(NS);
if (NS > 0) {
for (Map.Entry<String, BatteryStats.Uid.Pkg.Serv> sent
: ps.serviceStats.entrySet()) {
out.writeString(sent.getKey());
BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
long time = ss.getStartTimeToNowLocked(NOW);
out.writeLong(time);
out.writeLong(time - ss.loadedStartTime);
out.writeInt(ss.starts);
out.writeInt(ss.starts - ss.loadedStarts);
out.writeInt(ss.launches);
out.writeInt(ss.launches - ss.loadedLaunches);
}
}
}
}
}
stream.write(out.marshall());
out.recycle();
stream.flush();
stream.close();
mBackupFile.delete();
} catch(java.io.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;
}
}
}
void readLocked() {
uidStats.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();
final int version = in.readInt();
//Log.i("foo", "Read version: got " + version + ", expecting " + VERSION);
if (version != VERSION) {
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();
//Log.i("foo", "Start count: " + mStartCount);
mStartCount++;
final int NU = in.readInt();
//Log.i("foo", "Number uids: " + NU);
for (int iu=0; iu<NU; iu++) {
int uid = in.readInt();
//Log.i("foo", "Uid #" + iu + ": " + uid);
Uid u = new Uid();
uidStats.put(uid, u);
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).readLocked(in);
}
if (in.readInt() != 0) {
u.getWakeTimerLocked(wlName, WAKE_TYPE_PARTIAL).readLocked(in);
}
if (in.readInt() != 0) {
u.getWakeTimerLocked(wlName, WAKE_TYPE_WINDOW).readLocked(in);
}
}
int NP = in.readInt();
for (int ip=0; ip<NP; ip++) {
String procName = in.readString();
Uid.Proc p = u.getProcessStatsLocked(procName);
p.userTime = p.loadedUserTime = in.readLong();
p.lastUserTime = in.readLong();
p.systemTime = p.loadedSystemTime = in.readLong();
p.lastSystemTime = in.readLong();
p.starts = p.loadedStarts = in.readInt();
p.lastStarts = in.readInt();
}
NP = in.readInt();
for (int ip=0; ip<NP; ip++) {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
p.wakeups = p.loadedWakeups = in.readInt();
p.lastWakeups = 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.startTime = s.loadedStartTime = in.readLong();
s.lastStartTime = in.readLong();
s.starts = s.loadedStarts = in.readInt();
s.lastStarts = in.readInt();
s.launches = s.loadedLaunches = in.readInt();
s.lastLaunches = in.readInt();
}
}
}
} catch(java.io.IOException e) {
Log.e("BatteryStats", "Error reading battery statistics", e);
}
}
}