1147 lines
40 KiB
Java
1147 lines
40 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|