diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4857533a13375..cf0caed0df6ac 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -31,7 +31,6 @@ import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.Printer; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TimeUtils; import com.android.internal.os.BatterySipper; @@ -601,6 +600,7 @@ public abstract class BatteryStats implements Parcelable { public int states; public static final int STATE2_VIDEO_ON_FLAG = 1<<0; + public static final int STATE2_LOW_POWER_FLAG = 1<<1; public int states2; // The wake lock that was acquired at this point. @@ -622,8 +622,11 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_TOP = 0x0003; // Event is about an application package that is at the top of the screen. public static final int EVENT_SYNC = 0x0004; + // Events for all additional wake locks aquired/release within a wake block. + // These are not generated by default. + public static final int EVENT_WAKE_LOCK = 0x0005; // Number of event types. - public static final int EVENT_COUNT = 0x0005; + public static final int EVENT_COUNT = 0x0006; // Mask to extract out only the type part of the event. public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); @@ -635,6 +638,8 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_TOP_FINISH = EVENT_TOP | EVENT_FLAG_FINISH; public static final int EVENT_SYNC_START = EVENT_SYNC | EVENT_FLAG_START; public static final int EVENT_SYNC_FINISH = EVENT_SYNC | EVENT_FLAG_FINISH; + public static final int EVENT_WAKE_LOCK_START = EVENT_WAKE_LOCK | EVENT_FLAG_START; + public static final int EVENT_WAKE_LOCK_FINISH = EVENT_WAKE_LOCK | EVENT_FLAG_FINISH; // For CMD_EVENT. public int eventCode; @@ -887,6 +892,11 @@ public abstract class BatteryStats implements Parcelable { return true; } + public void removeEvents(int code) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + mActiveEvents[idx] = null; + } + public HashMap getStateForEvent(int code) { return mActiveEvents[code]; } @@ -996,6 +1006,21 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenBrightnessTime(int brightnessBin, long elapsedRealtimeUs, int which); + /** + * Returns the time in microseconds that low power mode has been enabled while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which); + + /** + * Returns the number of times that low power mode was enabled. + * + * {@hide} + */ + public abstract int getLowPowerModeEnabledCount(int which); + /** * Returns the time in microseconds that the phone has been on while the device was * running on battery. @@ -1157,14 +1182,15 @@ public abstract class BatteryStats implements Parcelable { public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] { new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"), + new BitDescription(HistoryItem.STATE2_LOW_POWER_FLAG, "low_power", "lp"), }; public static final String[] HISTORY_EVENT_NAMES = new String[] { - "null", "proc", "fg", "top", "sync" + "null", "proc", "fg", "top", "sync", "wake_lock_in" }; public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] { - "Enl", "Epr", "Efg", "Etp", "Esy" + "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl" }; /** @@ -1630,11 +1656,12 @@ public abstract class BatteryStats implements Parcelable { final long totalUptime = computeUptime(rawUptime, which); final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); + final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); - + StringBuilder sb = new StringBuilder(128); SparseArray uidStats = getUidStats(); @@ -1699,7 +1726,8 @@ public abstract class BatteryStats implements Parcelable { mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes, fullWakeLockTimeTotal, partialWakeLockTimeTotal, 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which), - getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000); + getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000, + lowPowerModeEnabledTime / 1000); // Dump screen brightness stats Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -2092,32 +2120,20 @@ public abstract class BatteryStats implements Parcelable { final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); + final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); sb.setLength(0); - sb.append(prefix); - sb.append(" Interactive: "); formatTimeMs(sb, interactiveTime / 1000); - sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime)); - sb.append(")"); - pw.println(sb.toString()); - sb.setLength(0); sb.append(prefix); sb.append(" Screen on: "); formatTimeMs(sb, screenOnTime / 1000); sb.append("("); sb.append(formatRatioLocked(screenOnTime, whichBatteryRealtime)); sb.append(") "); sb.append(getScreenOnCount(which)); - sb.append("x, Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); + sb.append("x, Interactive: "); formatTimeMs(sb, interactiveTime / 1000); + sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime)); sb.append(")"); pw.println(sb.toString()); - if (phoneOnTime != 0) { - sb.setLength(0); - sb.append(prefix); - sb.append(" Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); - sb.append(") "); sb.append(getPhoneOnCount(which)); - } sb.setLength(0); sb.append(prefix); sb.append(" Screen brightnesses:"); @@ -2139,7 +2155,24 @@ public abstract class BatteryStats implements Parcelable { } if (!didOne) sb.append(" (no activity)"); pw.println(sb.toString()); - + if (lowPowerModeEnabledTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Low power mode enabled: "); + formatTimeMs(sb, lowPowerModeEnabledTime / 1000); + sb.append("("); + sb.append(formatRatioLocked(lowPowerModeEnabledTime, whichBatteryRealtime)); + sb.append(")"); + pw.println(sb.toString()); + } + if (phoneOnTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); + sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); + sb.append(") "); sb.append(getPhoneOnCount(which)); + } + // Calculate wakelock times across all uids. long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; @@ -3004,6 +3037,8 @@ public abstract class BatteryStats implements Parcelable { pw.print(rec.numReadInts); pw.print(") "); } else { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); if (lastTime < 0) { pw.print(rec.time - baseTime); } else { @@ -3190,6 +3225,7 @@ public abstract class BatteryStats implements Parcelable { } pw.println(); oldState = rec.states; + oldState2 = rec.states2; } } } @@ -3266,21 +3302,25 @@ public abstract class BatteryStats implements Parcelable { if (rec.cmd == HistoryItem.CMD_CURRENT_TIME || rec.cmd == HistoryItem.CMD_RESET) { printed = true; + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = HistoryItem.CMD_UPDATE; } else if (rec.currentTime != 0) { printed = true; byte cmd = rec.cmd; rec.cmd = HistoryItem.CMD_CURRENT_TIME; - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); rec.cmd = cmd; } if (tracker != null) { - int oldCode = rec.eventCode; - HistoryTag oldTag = rec.eventTag; + if (rec.cmd != HistoryItem.CMD_UPDATE) { + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = HistoryItem.CMD_UPDATE; + } + int oldEventCode = rec.eventCode; + HistoryTag oldEventTag = rec.eventTag; rec.eventTag = new HistoryTag(); for (int i=0; i active @@ -3296,24 +3336,18 @@ public abstract class BatteryStats implements Parcelable { rec.eventTag.string = ent.getKey(); rec.eventTag.uid = uids.keyAt(j); rec.eventTag.poolIdx = uids.valueAt(j); - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); + rec.wakeReasonTag = null; + rec.wakelockTag = null; } } } - rec.eventCode = oldCode; - rec.eventTag = oldTag; + rec.eventCode = oldEventCode; + rec.eventTag = oldEventTag; tracker = null; } } - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); } else if (rec.eventCode != HistoryItem.EVENT_NONE) { diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index cb3d528198254..69b828f82ac8f 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -55,6 +55,14 @@ public abstract class PowerManagerInternal { */ public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis); + public abstract boolean getLowPowerModeEnabled(); + + public interface LowPowerModeListener { + public void onLowPowerModeChanged(boolean enabled); + } + + public abstract void registerLowPowerModeObserver(LowPowerModeListener listener); + // TODO: Remove this and retrieve as a local service instead. public abstract void setPolicy(WindowManagerPolicy policy); } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 0454749572e28..0769b085b50c5 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -46,14 +46,15 @@ interface IBatteryStats { void noteStartWakelock(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging); - void noteStopWakelock(int uid, int pid, String name, int type); + void noteStopWakelock(int uid, int pid, String name, String historyName, int type); void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, String historyName, int type, boolean unimportantForLogging); - void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, int type, - in WorkSource newWs, int newPid, String newName, + void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, String histyoryName, + int type, in WorkSource newWs, int newPid, String newName, String newHistoryName, int newType, boolean newUnimportantForLogging); - void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type); + void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, String historyName, + int type); void noteVibratorOn(int uid, long durationMillis); void noteVibratorOff(int uid); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7bd5b12fc7913..956c86d9dfd4e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -48,7 +48,6 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.view.Display; @@ -89,7 +88,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 105 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 106 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -236,6 +235,7 @@ public final class BatteryStatsImpl extends BatteryStats { int mWakeLockNesting; boolean mWakeLockImportant; + public boolean mRecordAllWakeLocks; int mScreenState = Display.STATE_UNKNOWN; StopwatchTimer mScreenOnTimer; @@ -246,6 +246,9 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mInteractive; StopwatchTimer mInteractiveTimer; + boolean mLowPowerModeEnabled; + StopwatchTimer mLowPowerModeEnabledTimer; + boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; @@ -2314,6 +2317,14 @@ public final class BatteryStatsImpl extends BatteryStats { private String mInitialAcquireWakeName; private int mInitialAcquireWakeUid = -1; + public void setRecordAllWakeLocksLocked(boolean enabled) { + mRecordAllWakeLocks = enabled; + if (!enabled) { + // Clear out any existing state. + mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK); + } + } + public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging, long elapsedRealtime, long uptime) { uid = mapUid(uid); @@ -2321,27 +2332,33 @@ public final class BatteryStatsImpl extends BatteryStats { // Only care about partial wake locks, since full wake locks // will be canceled when the user puts the screen to sleep. aggregateLastWakeupUptimeLocked(uptime); + historyName = historyName == null || mRecordAllWakeLocks ? name : historyName; if (mWakeLockNesting == 0) { mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: " + Integer.toHexString(mHistoryCur.states)); mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = mInitialAcquireWakeName - = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName; mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; mWakeLockImportant = !unimportantForLogging; addHistoryRecordLocked(elapsedRealtime, uptime); - } else if (!mWakeLockImportant && !unimportantForLogging) { + } else if (!mRecordAllWakeLocks && !mWakeLockImportant && !unimportantForLogging) { if (mHistoryLastWritten.wakelockTag != null) { // We'll try to update the last tag. mHistoryLastWritten.wakelockTag = null; mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = mInitialAcquireWakeName - = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName; mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; addHistoryRecordLocked(elapsedRealtime, uptime); } mWakeLockImportant = true; + } else if (mRecordAllWakeLocks) { + if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName, + uid, 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_START, + historyName, uid); } mWakeLockNesting++; } @@ -2354,24 +2371,33 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime, - long uptime) { + public void noteStopWakeLocked(int uid, int pid, String name, String historyName, int type, + long elapsedRealtime, long uptime) { uid = mapUid(uid); if (type == WAKE_TYPE_PARTIAL) { mWakeLockNesting--; + historyName = historyName == null || mRecordAllWakeLocks ? name : historyName; if (mWakeLockNesting == 0) { mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: " + Integer.toHexString(mHistoryCur.states)); - if ((name != null && !name.equals(mInitialAcquireWakeName)) + if (mRecordAllWakeLocks + || (historyName != null && !historyName.equals(mInitialAcquireWakeName)) || uid != mInitialAcquireWakeUid) { mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = name; + mHistoryCur.wakelockTag.string = historyName; mHistoryCur.wakelockTag.uid = uid; } mInitialAcquireWakeName = null; mInitialAcquireWakeUid = -1; addHistoryRecordLocked(elapsedRealtime, uptime); + } else if (mRecordAllWakeLocks) { + if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, + uid, 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_FINISH, + historyName, uid); } } if (uid >= 0) { @@ -2391,8 +2417,8 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name, int type, - WorkSource newWs, int newPid, String newName, + public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name, + String historyName, int type, WorkSource newWs, int newPid, String newName, String newHistoryName, int newType, boolean newUnimportantForLogging) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -2406,16 +2432,17 @@ public final class BatteryStatsImpl extends BatteryStats { } final int NO = ws.size(); for (int i=0; i: optional name of package to filter output by."); } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { + i++; + if (i >= args.length) { + pw.println("Missing option argument for " + (enable ? "--enable" : "--disable")); + dumpHelp(pw); + return -1; + } + if ("full-wake-history".equals(args[i])) { + synchronized (mStats) { + mStats.setRecordAllWakeLocksLocked(enable); + } + } else { + pw.println("Unknown enable/disable option: " + args[i]); + dumpHelp(pw); + return -1; + } + return i; + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -662,6 +698,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { pw.println("Battery stats written."); noOutput = true; } + } else if ("--enable".equals(arg)) { + i = doEnableOrDisable(pw, i, args, true); + if (i < 0) { + return; + } + pw.println("Enabled: " + args[i]); + return; + } else if ("--disable".equals(arg)) { + i = doEnableOrDisable(pw, i, args, false); + if (i < 0) { + return; + } + pw.println("Disabled: " + args[i]); + return; } else if ("-h".equals(arg)) { dumpHelp(pw); return; diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 2d6cc7c97c63f..e244bdea80e73 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -190,14 +190,14 @@ final class Notifier { + ", workSource=" + newWorkSource); } try { - mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, monitorType, - newWorkSource, newOwnerPid, newTag, newHistoryTag, + mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag, + monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag, newMonitorType, unimportantForLogging); } catch (RemoteException ex) { // Ignore } } else { - onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource); + onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag); onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid, newWorkSource, newHistoryTag); } @@ -207,7 +207,7 @@ final class Notifier { * Called when a wake lock is released. */ public void onWakeLockReleased(int flags, String tag, String packageName, - int ownerUid, int ownerPid, WorkSource workSource) { + int ownerUid, int ownerPid, WorkSource workSource, String historyTag) { if (DEBUG) { Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag + "\", packageName=" + packageName @@ -218,9 +218,10 @@ final class Notifier { try { final int monitorType = getBatteryStatsWakeLockMonitorType(flags); if (workSource != null) { - mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType); + mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, historyTag, + monitorType); } else { - mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType); + mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, historyTag, monitorType); mAppOps.finishOperation(AppOpsManager.getToken(mAppOps), AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 03941c66b0bc0..f0b7861ac9a5c 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -18,6 +18,7 @@ package com.android.server.power; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BackgroundThread; import com.android.server.BatteryService; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -400,7 +401,10 @@ public final class PowerManagerService extends com.android.server.SystemService private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE; // If true, the device is in low power mode. - private static boolean mLowPowerModeEnabled; + private boolean mLowPowerModeEnabled; + + private final ArrayList mLowPowerModeListeners + = new ArrayList(); private native void nativeInit(); @@ -623,11 +627,24 @@ public final class PowerManagerService extends com.android.server.SystemService Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, + final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE, 0) != 0; if (lowPowerModeEnabled != mLowPowerModeEnabled) { powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); mLowPowerModeEnabled = lowPowerModeEnabled; + BackgroundThread.getHandler().post(new Runnable() { + @Override + public void run() { + ArrayList listeners; + synchronized (mLock) { + listeners = new ArrayList( + mLowPowerModeListeners); + } + for (int i=0; i