summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/os/BatteryStats.java47
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl4
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java285
-rw-r--r--services/java/com/android/server/PowerManagerService.java28
-rw-r--r--services/java/com/android/server/WindowManagerService.java4
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java86
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java7
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java7
-rw-r--r--tests/BatteryWaster/res/layout/battery_waster.xml10
-rw-r--r--tests/BatteryWaster/res/values/strings.xml2
-rw-r--r--tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java67
13 files changed, 494 insertions, 71 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d66e98b..d5741fc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -365,7 +365,8 @@ public class ActivityManager {
/**
* The time when the service was first made active, either by someone
- * starting or binding to it.
+ * starting or binding to it. This
+ * is in units of {@link android.os.SystemClock#elapsedRealtime()}.
*/
public long activeSince;
@@ -387,7 +388,8 @@ public class ActivityManager {
/**
* The time when there was last activity in the service (either
- * explicit requests to start it or clients binding to it).
+ * explicit requests to start it or clients binding to it). This
+ * is in units of {@link android.os.SystemClock#uptimeMillis()}.
*/
public long lastActivityTime;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a699388..a0a3bdf 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -290,6 +290,11 @@ public abstract class BatteryStats implements Parcelable {
*/
public static abstract class Proc {
+ public static class ExcessiveWake {
+ public long overTime;
+ public long usedTime;
+ }
+
/**
* Returns the total time (in 1/100 sec) spent executing in user code.
*
@@ -326,6 +331,10 @@ public abstract class BatteryStats implements Parcelable {
* @see BatteryStats#getCpuSpeedSteps()
*/
public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
+
+ public abstract int countExcessiveWakes();
+
+ public abstract ExcessiveWake getExcessiveWake(int i);
}
/**
@@ -421,6 +430,8 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
public static final int STATE_AUDIO_ON_FLAG = 1<<19;
public static final int STATE_VIDEO_ON_FLAG = 1<<18;
+ public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
+ public static final int STATE_SENSOR_ON_FLAG = 1<<16;
public int states;
@@ -470,6 +481,16 @@ public abstract class BatteryStats implements Parcelable {
batteryVoltage = o.batteryVoltage;
states = o.states;
}
+
+ public boolean same(HistoryItem o) {
+ return batteryLevel == o.batteryLevel
+ && batteryStatus == o.batteryStatus
+ && batteryHealth == o.batteryHealth
+ && batteryPlugType == o.batteryPlugType
+ && batteryTemperature == o.batteryTemperature
+ && batteryVoltage == o.batteryVoltage
+ && states == o.states;
+ }
}
public static final class BitDescription {
@@ -633,6 +654,8 @@ public abstract class BatteryStats implements Parcelable {
new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+ new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock"),
+ new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor"),
new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
SCREEN_BRIGHTNESS_NAMES),
@@ -1376,7 +1399,6 @@ public abstract class BatteryStats implements Parcelable {
pw.println(getDischargeStartLevel());
pw.print(prefix); pw.print(" Discharge cycle current level: ");
pw.println(getDischargeCurrentLevel());
- } else {
pw.print(prefix); pw.println(" Device is currently plugged into power");
pw.print(prefix); pw.print(" Last discharge cycle start level: ");
pw.println(getDischargeStartLevel());
@@ -1384,6 +1406,13 @@ public abstract class BatteryStats implements Parcelable {
pw.println(getDischargeCurrentLevel());
}
pw.println(" ");
+ } else {
+ pw.print(prefix); pw.println(" Device battery use since last full charge");
+ pw.print(prefix); pw.print(" Amount discharged (lower bound): ");
+ pw.println(getLowDischargeAmountSinceCharge());
+ pw.print(prefix); pw.print(" Amount discharged (upper bound): ");
+ pw.println(getHighDischargeAmountSinceCharge());
+ pw.println(" ");
}
@@ -1524,12 +1553,16 @@ public abstract class BatteryStats implements Parcelable {
long userTime;
long systemTime;
int starts;
+ int numExcessive;
userTime = ps.getUserTime(which);
systemTime = ps.getSystemTime(which);
starts = ps.getStarts(which);
+ numExcessive = which == STATS_SINCE_CHARGED
+ ? ps.countExcessiveWakes() : 0;
- if (userTime != 0 || systemTime != 0 || starts != 0) {
+ if (userTime != 0 || systemTime != 0 || starts != 0
+ || numExcessive != 0) {
sb.setLength(0);
sb.append(prefix); sb.append(" Proc ");
sb.append(ent.getKey()); sb.append(":\n");
@@ -1539,6 +1572,16 @@ public abstract class BatteryStats implements Parcelable {
sb.append(prefix); sb.append(" "); sb.append(starts);
sb.append(" proc starts");
pw.println(sb.toString());
+ for (int e=0; e<numExcessive; e++) {
+ Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
+ if (ew != null) {
+ pw.print(prefix); pw.print(" * Killed for wake lock use: ");
+ pw.print(ew.usedTime); pw.print("ms over ");
+ pw.print(ew.overTime); pw.print("ms (");
+ pw.print((ew.usedTime*100)/ew.overTime);
+ pw.println("%)");
+ }
+ }
uidActivity = true;
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d040d3f..1620778 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,8 +22,8 @@ import android.telephony.SignalStrength;
interface IBatteryStats {
byte[] getStatistics();
- void noteStartWakelock(int uid, String name, int type);
- void noteStopWakelock(int uid, String name, int type);
+ void noteStartWakelock(int uid, int pid, String name, int type);
+ void noteStopWakelock(int uid, int pid, String name, int type);
/* DO NOT CHANGE the position of noteStartSensor without updating
SensorService.cpp */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b3323e9..2f26135 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -27,6 +27,7 @@ import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
+import android.os.BatteryStats.Uid.Proc.ExcessiveWake;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -63,7 +64,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 49;
+ private static final int VERSION = 50;
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 1000;
@@ -107,6 +108,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int mNumHistoryItems;
HistoryItem mHistory;
HistoryItem mHistoryEnd;
+ HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -451,7 +453,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
mTotalTime = mLoadedTime = mLastTime = 0;
mCount = mLoadedCount = mLastCount = 0;
if (detachIfReset) {
@@ -713,8 +715,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
- boolean reset(boolean detachIfReset) {
- super.reset(detachIfReset);
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ super.reset(stats, detachIfReset);
setStale();
return true;
}
@@ -749,7 +751,7 @@ public final class BatteryStatsImpl extends BatteryStats {
long mUpdateTime;
/**
- * The total time at which the timer was acquired, to determine if
+ * The total time at which the timer was acquired, to determine if it
* was actually held for an interesting duration.
*/
long mAcquireTime;
@@ -890,9 +892,14 @@ public final class BatteryStatsImpl extends BatteryStats {
return mCount;
}
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
boolean canDetach = mNesting <= 0;
- super.reset(canDetach && detachIfReset);
+ super.reset(stats, canDetach && detachIfReset);
+ if (mNesting > 0) {
+ mUpdateTime = stats.getBatteryRealtimeLocked(
+ SystemClock.elapsedRealtime() * 1000);
+ }
+ mAcquireTime = mTotalTime;
return canDetach;
}
@@ -1113,6 +1120,26 @@ public final class BatteryStatsImpl extends BatteryStats {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
+
+ // If the current time is basically the same as the last time,
+ // just collapse into one record.
+ if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+ // If the current is the same as the one before, then we no
+ // longer need the entry.
+ if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+ && mHistoryLastEnd.same(mHistoryCur)) {
+ mHistoryLastEnd.next = null;
+ mHistoryEnd.next = mHistoryCache;
+ mHistoryCache = mHistoryEnd;
+ mHistoryEnd = mHistoryLastEnd;
+ mHistoryLastEnd = null;
+ } else {
+ mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+ }
+ return;
+ }
+
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
// record changes to the battery level.
@@ -1121,6 +1148,7 @@ public final class BatteryStatsImpl extends BatteryStats {
return;
}
}
+
addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
}
@@ -1139,6 +1167,7 @@ public final class BatteryStatsImpl extends BatteryStats {
void addHistoryRecordLocked(HistoryItem rec) {
mNumHistoryItems++;
rec.next = null;
+ mHistoryLastEnd = mHistoryEnd;
if (mHistoryEnd != null) {
mHistoryEnd.next = rec;
mHistoryEnd = rec;
@@ -1151,7 +1180,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mHistory != null) {
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistory;
- mHistory = mHistoryEnd = null;
+ mHistory = mHistoryLastEnd = mHistoryEnd = null;
}
mNumHistoryItems = 0;
mHistoryBaseTime = 0;
@@ -1209,6 +1238,83 @@ public final class BatteryStatsImpl extends BatteryStats {
mBluetoothPingStart = -1;
}
+ int mWakeLockNesting;
+
+ public void noteStartWakeLocked(int uid, int pid, String name, int type) {
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mWakeLockNesting++;
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+ mWakeLockNesting--;
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteProcessDiedLocked(int uid, int pid) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.mPids.remove(pid);
+ }
+ }
+
+ public long getProcessWakeTime(int uid, int pid, long realtime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ Uid.Pid p = u.mPids.get(pid);
+ if (p != null) {
+ return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+ }
+ }
+ return 0;
+ }
+
+ public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.reportExcessiveWakeLocked(proc, overTime, usedTime);
+ }
+ }
+
+ int mSensorNesting;
+
+ public void noteStartSensorLocked(int uid, int sensor) {
+ if (mSensorNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mSensorNesting++;
+ getUidStatsLocked(uid).noteStartSensor(sensor);
+ }
+
+ public void noteStopSensorLocked(int uid, int sensor) {
+ mSensorNesting--;
+ if (mSensorNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ getUidStatsLocked(uid).noteStopSensor(sensor);
+ }
+
int mGpsNesting;
public void noteStartGpsLocked(int uid) {
@@ -1244,6 +1350,10 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this);
}
+
+ // Fake a wake lock, so we consider the device waked as long
+ // as the screen is on.
+ noteStartWakeLocked(-1, -1, "dummy", 0);
}
}
@@ -1258,6 +1368,8 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
}
+
+ noteStopWakeLocked(-1, -1, "dummy", 0);
}
}
@@ -1798,6 +1910,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
+ /**
+ * The transient wake stats we have collected for this uid's pids.
+ */
+ final SparseArray<Pid> mPids = new SparseArray<Pid>();
+
public Uid(int uid) {
mUid = uid;
mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
@@ -2081,27 +2198,27 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean active = false;
if (mWifiTurnedOnTimer != null) {
- active |= !mWifiTurnedOnTimer.reset(false);
+ active |= !mWifiTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiTurnedOn;
}
if (mFullWifiLockTimer != null) {
- active |= !mFullWifiLockTimer.reset(false);
+ active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mFullWifiLockOut;
}
if (mScanWifiLockTimer != null) {
- active |= !mScanWifiLockTimer.reset(false);
+ active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mScanWifiLockOut;
}
if (mWifiMulticastTimer != null) {
- active |= !mWifiMulticastTimer.reset(false);
+ active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiMulticastEnabled;
}
if (mAudioTurnedOnTimer != null) {
- active |= !mAudioTurnedOnTimer.reset(false);
+ active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
- active |= !mVideoTurnedOnTimer.reset(false);
+ active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mVideoTurnedOn;
}
@@ -2146,6 +2263,14 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mProcessStats.clear();
}
+ if (mPids.size() > 0) {
+ for (int i=0; !active && i<mPids.size(); i++) {
+ Pid pid = mPids.valueAt(i);
+ if (pid.mWakeStart != 0) {
+ active = true;
+ }
+ }
+ }
if (mPackageStats.size() > 0) {
Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
while (it.hasNext()) {
@@ -2164,6 +2289,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mPackageStats.clear();
}
+ mPids.clear();
+
if (!active) {
if (mWifiTurnedOnTimer != null) {
mWifiTurnedOnTimer.detach();
@@ -2412,13 +2539,13 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
- wlactive |= !mTimerFull.reset(false);
+ wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false);
}
if (mTimerPartial != null) {
- wlactive |= !mTimerPartial.reset(false);
+ wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false);
}
if (mTimerWindow != null) {
- wlactive |= !mTimerWindow.reset(false);
+ wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false);
}
if (!wlactive) {
if (mTimerFull != null) {
@@ -2486,7 +2613,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
boolean reset() {
- if (mTimer.reset(true)) {
+ if (mTimer.reset(BatteryStatsImpl.this, true)) {
mTimer = null;
return true;
}
@@ -2598,6 +2725,8 @@ public final class BatteryStatsImpl extends BatteryStats {
SamplingCounter[] mSpeedBins;
+ ArrayList<ExcessiveWake> mExcessiveWake;
+
Proc() {
mUnpluggables.add(this);
mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
@@ -2624,6 +2753,58 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ public int countExcessiveWakes() {
+ return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+ }
+
+ public ExcessiveWake getExcessiveWake(int i) {
+ if (mExcessiveWake != null) {
+ return mExcessiveWake.get(i);
+ }
+ return null;
+ }
+
+ public void addExcessiveWake(long overTime, long usedTime) {
+ if (mExcessiveWake == null) {
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ }
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = overTime;
+ ew.usedTime = usedTime;
+ mExcessiveWake.add(ew);
+ }
+
+ void writeExcessiveWakeToParcelLocked(Parcel out) {
+ if (mExcessiveWake == null) {
+ out.writeInt(0);
+ return;
+ }
+
+ final int N = mExcessiveWake.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = mExcessiveWake.get(i);
+ out.writeLong(ew.overTime);
+ out.writeLong(ew.usedTime);
+ }
+ }
+
+ void readExcessiveWakeFromParcelLocked(Parcel in) {
+ final int N = in.readInt();
+ if (N == 0) {
+ mExcessiveWake = null;
+ return;
+ }
+
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = in.readLong();
+ ew.usedTime = in.readLong();
+ mExcessiveWake.add(ew);
+ }
+ }
+
void writeToParcelLocked(Parcel out) {
out.writeLong(mUserTime);
out.writeLong(mSystemTime);
@@ -2648,6 +2829,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(0);
}
}
+
+ writeExcessiveWakeToParcelLocked(out);
}
void readFromParcelLocked(Parcel in) {
@@ -2676,6 +2859,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
}
}
+
+ readExcessiveWakeFromParcelLocked(in);
}
public BatteryStatsImpl getBatteryStats() {
@@ -3153,6 +3338,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ public class Pid {
+ long mWakeSum;
+ long mWakeStart;
+ }
+
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
@@ -3167,6 +3357,15 @@ public final class BatteryStatsImpl extends BatteryStats {
return ps;
}
+ public Pid getPidStatsLocked(int pid) {
+ Pid p = mPids.get(pid);
+ if (p == null) {
+ p = new Pid();
+ mPids.put(pid, p);
+ }
+ return p;
+ }
+
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
@@ -3259,18 +3458,36 @@ public final class BatteryStatsImpl extends BatteryStats {
return t;
}
- public void noteStartWakeLocked(String name, int type) {
+ public void noteStartWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0) {
+ Pid p = getPidStatsLocked(pid);
+ p.mWakeStart = SystemClock.elapsedRealtime();
+ }
}
- public void noteStopWakeLocked(String name, int type) {
+ public void noteStopWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0) {
+ Pid p = mPids.get(pid);
+ if (p != null) {
+ p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
+ p.mWakeStart = 0;
+ }
+ }
+ }
+
+ public void reportExcessiveWakeLocked(String proc, long overTime, long usedTime) {
+ Proc p = getProcessStatsLocked(proc);
+ if (p != null) {
+ p.addExcessiveWake(overTime, usedTime);
+ }
}
public void noteStartSensor(int sensor) {
@@ -3372,6 +3589,10 @@ public final class BatteryStatsImpl extends BatteryStats {
return mOnBattery;
}
+ public boolean isScreenOn() {
+ return mScreenOn;
+ }
+
void initTimes() {
mBatteryRealtime = mTrackBatteryPastUptime = 0;
mBatteryUptime = mTrackBatteryPastRealtime = 0;
@@ -3384,24 +3605,24 @@ public final class BatteryStatsImpl extends BatteryStats {
public void resetAllStatsLocked() {
mStartCount = 0;
initTimes();
- mScreenOnTimer.reset(false);
+ mScreenOnTimer.reset(this, false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].reset(false);
+ mScreenBrightnessTimer[i].reset(this, false);
}
mInputEventCounter.reset(false);
- mPhoneOnTimer.reset(false);
- mAudioOnTimer.reset(false);
- mVideoOnTimer.reset(false);
+ mPhoneOnTimer.reset(this, false);
+ mAudioOnTimer.reset(this, false);
+ mVideoOnTimer.reset(this, false);
for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].reset(false);
+ mPhoneSignalStrengthsTimer[i].reset(this, false);
}
- mPhoneSignalScanningTimer.reset(false);
+ mPhoneSignalScanningTimer.reset(this, false);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].reset(false);
+ mPhoneDataConnectionsTimer[i].reset(this, false);
}
- mWifiOnTimer.reset(false);
- mWifiRunningTimer.reset(false);
- mBluetoothOnTimer.reset(false);
+ mWifiOnTimer.reset(this, false);
+ mWifiRunningTimer.reset(this, false);
+ mBluetoothOnTimer.reset(this, false);
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset()) {
@@ -4083,6 +4304,7 @@ public final class BatteryStatsImpl extends BatteryStats {
p.mUserTime = p.mLoadedUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
+ p.readExcessiveWakeFromParcelLocked(in);
}
NP = in.readInt();
@@ -4271,6 +4493,7 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(ps.mUserTime);
out.writeLong(ps.mSystemTime);
out.writeInt(ps.mStarts);
+ ps.writeExcessiveWakeToParcelLocked(out);
}
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2fb481c..4ee89cc 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -151,6 +151,7 @@ class PowerManagerService extends IPowerManager.Stub
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
private final int MY_UID;
+ private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;
@@ -309,7 +310,7 @@ class PowerManagerService extends IPowerManager.Stub
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, mTag);
+ MY_UID, MY_PID, mTag);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -434,11 +435,11 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- PowerManagerService()
- {
+ PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
- MY_UID = Binder.getCallingUid();
+ MY_UID = Process.myUid();
+ MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
// XXX remove this when the kernel doesn't timeout wake locks
@@ -573,13 +574,13 @@ class PowerManagerService extends IPowerManager.Stub
private class WakeLock implements IBinder.DeathRecipient
{
- WakeLock(int f, IBinder b, String t, int u) {
+ WakeLock(int f, IBinder b, String t, int u, int p) {
super();
flags = f;
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = Binder.getCallingPid();
+ pid = p;
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -631,21 +632,23 @@ class PowerManagerService extends IPowerManager.Stub
public void acquireWakeLock(int flags, IBinder lock, String tag) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
int acquireUid = -1;
+ int acquirePid = -1;
String acquireName = null;
int acquireType = -1;
@@ -657,7 +660,7 @@ class PowerManagerService extends IPowerManager.Stub
WakeLock wl;
boolean newlock;
if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid);
+ wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
@@ -730,13 +733,14 @@ class PowerManagerService extends IPowerManager.Stub
}
if (newlock) {
acquireUid = wl.uid;
+ acquirePid = wl.pid;
acquireName = wl.tag;
acquireType = wl.monitorType;
}
if (acquireType >= 0) {
try {
- mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+ mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
} catch (RemoteException e) {
// Ignore
}
@@ -756,6 +760,7 @@ class PowerManagerService extends IPowerManager.Stub
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
int releaseUid;
+ int releasePid;
String releaseName;
int releaseType;
@@ -800,13 +805,14 @@ class PowerManagerService extends IPowerManager.Stub
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
releaseUid = wl.uid;
+ releasePid = wl.pid;
releaseName = wl.tag;
releaseType = wl.monitorType;
if (releaseType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+ mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
} catch (RemoteException e) {
// Ignore
} finally {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 59deef3..e259887 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -8081,12 +8081,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (oldHold != newHold) {
try {
if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid,
+ mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid,
+ mBatteryStats.noteStartWakelock(newHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index dff6a8a..e5d1025 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -199,6 +199,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
+ // The rate at which we check for apps using excessive wake locks -- 15 mins.
+ static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -770,6 +773,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mDidAppSwitch;
/**
+ * Last time (in realtime) at which we checked for wake lock usage.
+ */
+ long mLastWakeLockCheckTime;
+
+ /**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
@@ -914,6 +922,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
AlertDialog mUidAlert;
@@ -1173,6 +1182,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
} break;
+ case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ checkExcessiveWakeLocksLocked(true);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ if (mSleeping) {
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+ }
+ }
+ } break;
}
}
};
@@ -2555,6 +2574,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mProcDeaths[0]++;
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteProcessDiedLocked(app.info.uid, pid);
+ }
+
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
@@ -3570,6 +3594,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
@@ -5375,6 +5402,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
+
+ // Initialize the wake times of all processes.
+ checkExcessiveWakeLocksLocked(false);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
}
}
@@ -5424,6 +5457,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWindowManager.setEventDispatching(true);
mSleeping = false;
mMainStack.resumeTopActivityLocked(null);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
}
}
@@ -11259,6 +11293,52 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ final void checkExcessiveWakeLocksLocked(boolean doKills) {
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ if (mLastWakeLockCheckTime == 0) {
+ doKills = false;
+ }
+ if (stats.isScreenOn()) {
+ doKills = false;
+ }
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long timeSince = curRealtime - mLastWakeLockCheckTime;
+ mLastWakeLockCheckTime = curRealtime;
+ if (timeSince < 5*60*1000) {
+ doKills = false;
+ }
+ int i = mLruProcesses.size();
+ while (i > 0) {
+ i--;
+ ProcessRecord app = mLruProcesses.get(i);
+ if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+ long wtime;
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, curRealtime);
+ }
+ long timeUsed = wtime - app.lastWakeTime;
+ Slog.i(TAG, "Wake for " + app + ": over "
+ + timeSince + " used " + timeUsed
+ + " (" + ((timeUsed*100)/timeSince) + "%)");
+ // If a process has held a wake lock for more
+ // than 50% of the time during this period,
+ // that sounds pad. Kill!
+ if (doKills && timeSince > 0
+ && ((timeUsed*100)/timeSince) >= 50) {
+ Slog.i(TAG, "Excessive wake lock in " + app.processName
+ + " (pid " + app.pid + "): held " + timeUsed
+ + " during " + timeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive wake lock");
+ Process.killProcessQuiet(app.pid);
+ } else {
+ app.lastWakeTime = wtime;
+ }
+ }
+ }
+ }
+
private final boolean updateOomAdjLocked(
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
@@ -11281,6 +11361,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Likewise do a gc when an app is moving in to the
// background (such as a service stopping).
scheduleAppGcLocked(app);
+ // And note its current wake lock time.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
+ }
}
app.setRawAdj = app.curRawAdj;
}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 37da6f7..7314e04 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -93,31 +93,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return data;
}
- public void noteStartWakelock(int uid, String name, int type) {
+ public void noteStartWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+ mStats.noteStartWakeLocked(uid, pid, name, type);
}
}
- public void noteStopWakelock(int uid, String name, int type) {
+ public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+ mStats.noteStopWakeLocked(uid, pid, name, type);
}
}
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+ mStats.noteStartSensorLocked(uid, sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+ mStats.noteStopSensorLocked(uid, sensor);
}
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 18b1acb..73e489f 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -74,6 +74,7 @@ class ProcessRecord {
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
BroadcastRecord curReceiver;// receiver currently running in the app
+ long lastWakeTime; // How long proc held wake lock at last check
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
@@ -158,7 +159,7 @@ class ProcessRecord {
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
- pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -177,6 +178,10 @@ class ProcessRecord {
pw.print(" persistentActivities="); pw.println(persistentActivities);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+ pw.print(" lastRequestedGc="); pw.print(lastRequestedGc);
+ pw.print(" lastLowMemory="); pw.print(lastLowMemory);
+ pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75365ad..35f1875 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -149,7 +149,9 @@ class ServiceRecord extends Binder {
pw.print(" foregroundId="); pw.print(foregroundId);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
- pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
+ pw.print(prefix); pw.print("createTime=");
+ pw.print(createTime-SystemClock.elapsedRealtime());
+ pw.print(" lastActivity="); pw.print(lastActivity-now);
pw.print(" executingStart="); pw.print(executingStart-now);
pw.print(" restartTime="); pw.println(restartTime);
if (startRequested || lastStartId != 0) {
@@ -213,7 +215,8 @@ class ServiceRecord extends Binder {
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = lastActivity = SystemClock.uptimeMillis();
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index e1cb6bf..36aa68b 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -30,6 +30,16 @@
android:text="@string/waste_away"
/>
+ <CheckBox android:id="@+id/checkbox_wake"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="25dp"
+ android:layout_marginTop="25dp"
+ android:textSize="18sp"
+ android:textColor="#ffffffff"
+ android:text="@string/wake_away"
+ />
+
<ScrollView android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="0px"
diff --git a/tests/BatteryWaster/res/values/strings.xml b/tests/BatteryWaster/res/values/strings.xml
index 46c5fa1..a3b849a 100644
--- a/tests/BatteryWaster/res/values/strings.xml
+++ b/tests/BatteryWaster/res/values/strings.xml
@@ -18,5 +18,7 @@
<string name="waste_away">Discharge my battery!</string>
+ <string name="wake_away">Keep my device awake!</string>
+
</resources>
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 8ea7e00..499330f 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -41,6 +41,8 @@ public class BatteryWaster extends Activity {
PowerManager.WakeLock mWakeLock;
SpinThread mThread;
+ boolean mWasting, mWaking;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -50,6 +52,7 @@ public class BatteryWaster extends Activity {
setContentView(R.layout.battery_waster);
findViewById(R.id.checkbox).setOnClickListener(mClickListener);
+ findViewById(R.id.checkbox_wake).setOnClickListener(mWakeClickListener);
mLog = (TextView)findViewById(R.id.log);
mDateFormat = DateFormat.getInstance();
@@ -67,9 +70,18 @@ public class BatteryWaster extends Activity {
@Override
public void onPause() {
+ super.onPause();
stopRunning();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
View.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
CheckBox checkbox = (CheckBox)v;
@@ -81,23 +93,54 @@ public class BatteryWaster extends Activity {
}
};
+ View.OnClickListener mWakeClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkbox = (CheckBox)v;
+ if (checkbox.isChecked()) {
+ mWaking = true;
+ updateWakeLock();
+ } else {
+ mWaking = false;
+ updateWakeLock();
+ }
+ }
+ };
+
void startRunning() {
- log("Start");
- registerReceiver(mReceiver, mFilter);
- mWakeLock.acquire();
- if (mThread == null) {
- mThread = new SpinThread();
- mThread.start();
+ if (!mWasting) {
+ log("Start");
+ registerReceiver(mReceiver, mFilter);
+ mWasting = true;
+ updateWakeLock();
+ if (mThread == null) {
+ mThread = new SpinThread();
+ mThread.start();
+ }
}
}
void stopRunning() {
- log("Stop");
- unregisterReceiver(mReceiver);
- mWakeLock.release();
- if (mThread != null) {
- mThread.quit();
- mThread = null;
+ if (mWasting) {
+ log("Stop");
+ unregisterReceiver(mReceiver);
+ mWasting = false;
+ updateWakeLock();
+ if (mThread != null) {
+ mThread.quit();
+ mThread = null;
+ }
+ }
+ }
+
+ void updateWakeLock() {
+ if (mWasting || mWaking) {
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ } else {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
}
}