From fdb1956ff71ff57fcdaafaaeb7f42c19de3d7c2f Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 11 Jul 2014 16:03:36 -0700 Subject: [PATCH] Fix issue #15681802: Missing RESET:TIME in complete battery histories But wait, there's more! - Keep track of sync durations in the aggregated stats. - Add events for users that are running and in the foreground. - Rework the activity manager's tracking of stuff using battery in the background to be based on proc stats, which allows it to be better about determing when it should reset its tracking of background work. - Also add tracking of scheduled job execution, like we are doing for syncs. - And once I started hooking battery stats in to JobSchedulerService, I found a few things I couldn't stop myself from changing: (1) make it very explicit that it doesn't start scheduling jobs until we have reached the point in system boot where third party apps are allowed to run, and (2) adjust the various for loops to not use iterators. Change-Id: I69d812e27bcfee9e58a614f0f6b1c7545d7530b1 --- core/java/android/os/BatteryStats.java | 133 ++++++++++- .../android/internal/app/IBatteryStats.aidl | 4 + .../android/internal/os/BatteryStatsImpl.java | 226 +++++++++++++++++- .../server/am/ActivityManagerService.java | 78 +++--- .../server/am/BatteryStatsService.java | 28 +++ .../com/android/server/am/ProcessRecord.java | 9 +- .../android/server/content/SyncManager.java | 6 +- .../server/job/JobSchedulerService.java | 142 +++++++---- .../android/server/job/JobServiceContext.java | 25 +- .../java/com/android/server/job/JobStore.java | 9 +- .../server/job/controllers/JobStatus.java | 12 + 11 files changed, 557 insertions(+), 115 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 7fd166059ac46..aab7b1f504ba9 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -119,6 +119,16 @@ public abstract class BatteryStats implements Parcelable { */ public static final int PROCESS_STATE = 12; + /** + * A constant indicating a sync timer + */ + public static final int SYNC = 13; + + /** + * A constant indicating a job timer + */ + public static final int JOB = 14; + /** * Include all of the data in the stats, including previously saved data. */ @@ -157,6 +167,8 @@ public abstract class BatteryStats implements Parcelable { private static final String FOREGROUND_DATA = "fg"; private static final String STATE_TIME_DATA = "st"; private static final String WAKELOCK_DATA = "wl"; + private static final String SYNC_DATA = "sy"; + private static final String JOB_DATA = "jb"; private static final String KERNEL_WAKELOCK_DATA = "kwl"; private static final String WAKEUP_REASON_DATA = "wr"; private static final String NETWORK_DATA = "nt"; @@ -272,6 +284,20 @@ public abstract class BatteryStats implements Parcelable { */ public abstract Map getWakelockStats(); + /** + * Returns a mapping containing sync statistics. + * + * @return a Map from Strings to Timer objects. + */ + public abstract Map getSyncStats(); + + /** + * Returns a mapping containing scheduled job statistics. + * + * @return a Map from Strings to Timer objects. + */ + public abstract Map getJobStats(); + /** * The statistics associated with a particular wake lock. */ @@ -660,13 +686,19 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_FOREGROUND = 0x0002; // Event is about an application package that is at the top of the screen. public static final int EVENT_TOP = 0x0003; - // Event is about an application package that is at the top of the screen. + // Event is about active sync operations. 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; + // Event is about an application executing a scheduled job. + public static final int EVENT_JOB = 0x0006; + // Events for users running. + public static final int EVENT_USER_RUNNING = 0x0007; + // Events for foreground user. + public static final int EVENT_USER_FOREGROUND = 0x0008; // Number of event types. - public static final int EVENT_COUNT = 0x0006; + public static final int EVENT_COUNT = 0x0009; // Mask to extract out only the type part of the event. public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); @@ -680,6 +712,14 @@ public abstract class BatteryStats implements Parcelable { 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; + public static final int EVENT_JOB_START = EVENT_JOB | EVENT_FLAG_START; + public static final int EVENT_JOB_FINISH = EVENT_JOB | EVENT_FLAG_FINISH; + public static final int EVENT_USER_RUNNING_START = EVENT_USER_RUNNING | EVENT_FLAG_START; + public static final int EVENT_USER_RUNNING_FINISH = EVENT_USER_RUNNING | EVENT_FLAG_FINISH; + public static final int EVENT_USER_FOREGROUND_START = + EVENT_USER_FOREGROUND | EVENT_FLAG_START; + public static final int EVENT_USER_FOREGROUND_FINISH = + EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH; // For CMD_EVENT. public int eventCode; @@ -1269,11 +1309,11 @@ public abstract class BatteryStats implements Parcelable { }; public static final String[] HISTORY_EVENT_NAMES = new String[] { - "null", "proc", "fg", "top", "sync", "wake_lock_in" + "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg" }; public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] { - "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl" + "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf" }; /** @@ -2080,10 +2120,9 @@ public abstract class BatteryStats implements Parcelable { } } - Map wakelocks = u.getWakelockStats(); + Map wakelocks = u.getWakelockStats(); if (wakelocks.size() > 0) { - for (Map.Entry ent - : wakelocks.entrySet()) { + for (Map.Entry ent : wakelocks.entrySet()) { Uid.Wakelock wl = ent.getValue(); String linePrefix = ""; sb.setLength(0); @@ -2105,6 +2144,32 @@ public abstract class BatteryStats implements Parcelable { } } + Map syncs = u.getSyncStats(); + if (syncs.size() > 0) { + for (Map.Entry ent : syncs.entrySet()) { + Timer timer = ent.getValue(); + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + int count = timer.getCountLocked(which); + if (totalTime != 0) { + dumpLine(pw, uid, category, SYNC_DATA, ent.getKey(), totalTime, count); + } + } + } + + Map jobs = u.getJobStats(); + if (jobs.size() > 0) { + for (Map.Entry ent : jobs.entrySet()) { + Timer timer = ent.getValue(); + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + int count = timer.getCountLocked(which); + if (totalTime != 0) { + dumpLine(pw, uid, category, JOB_DATA, ent.getKey(), totalTime, count); + } + } + } + SparseArray sensors = u.getSensorStats(); int NSE = sensors.size(); for (int ise=0; ise 0) { long totalFull = 0, totalPartial = 0, totalWindow = 0; int count = 0; - for (Map.Entry ent - : wakelocks.entrySet()) { + for (Map.Entry ent : wakelocks.entrySet()) { Uid.Wakelock wl = ent.getValue(); String linePrefix = ": "; sb.setLength(0); @@ -2998,6 +3062,56 @@ public abstract class BatteryStats implements Parcelable { } } + Map syncs = u.getSyncStats(); + if (syncs.size() > 0) { + for (Map.Entry ent : syncs.entrySet()) { + Timer timer = ent.getValue(); + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + int count = timer.getCountLocked(which); + sb.setLength(0); + sb.append(prefix); + sb.append(" Sync "); + sb.append(ent.getKey()); + sb.append(": "); + if (totalTime != 0) { + formatTimeMs(sb, totalTime); + sb.append("realtime ("); + sb.append(count); + sb.append(" times)"); + } else { + sb.append("(not used)"); + } + pw.println(sb.toString()); + uidActivity = true; + } + } + + Map jobs = u.getJobStats(); + if (syncs.size() > 0) { + for (Map.Entry ent : jobs.entrySet()) { + Timer timer = ent.getValue(); + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + int count = timer.getCountLocked(which); + sb.setLength(0); + sb.append(prefix); + sb.append(" Job "); + sb.append(ent.getKey()); + sb.append(": "); + if (totalTime != 0) { + formatTimeMs(sb, totalTime); + sb.append("realtime ("); + sb.append(count); + sb.append(" times)"); + } else { + sb.append("(not used)"); + } + pw.println(sb.toString()); + uidActivity = true; + } + } + SparseArray sensors = u.getSensorStats(); int NSE = sensors.size(); for (int ise=0; ise mWakelockStats = new ArrayMap(); + /** + * The statistics we have collected for this uid's syncs. + */ + final ArrayMap mSyncStats = new ArrayMap(); + + /** + * The statistics we have collected for this uid's jobs. + */ + final ArrayMap mJobStats = new ArrayMap(); + /** * The statistics we have collected for this uid's sensor activations. */ @@ -3871,6 +3930,16 @@ public final class BatteryStatsImpl extends BatteryStats { return mWakelockStats; } + @Override + public Map getSyncStats() { + return mSyncStats; + } + + @Override + public Map getJobStats() { + return mJobStats; + } + @Override public SparseArray getSensorStats() { return mSensorStats; @@ -4396,6 +4465,24 @@ public final class BatteryStatsImpl extends BatteryStats { active = true; } } + for (int is=mSyncStats.size()-1; is>=0; is--) { + StopwatchTimer timer = mSyncStats.valueAt(is); + if (timer.reset(false)) { + mSyncStats.removeAt(is); + timer.detach(); + } else { + active = true; + } + } + for (int ij=mJobStats.size()-1; ij>=0; ij--) { + StopwatchTimer timer = mJobStats.valueAt(ij); + if (timer.reset(false)) { + mJobStats.removeAt(ij); + timer.detach(); + } else { + active = true; + } + } for (int ise=mSensorStats.size()-1; ise>=0; ise--) { Sensor s = mSensorStats.valueAt(ise); if (s.reset()) { @@ -4497,6 +4584,22 @@ public final class BatteryStatsImpl extends BatteryStats { wakelock.writeToParcelLocked(out, elapsedRealtimeUs); } + int NS = mSyncStats.size(); + out.writeInt(NS); + for (int is=0; is MAX_WAKELOCKS_PER_UID) { + name = BATCHED_WAKELOCK_NAME; + t = mSyncStats.get(name); + } + if (t == null) { + t = new StopwatchTimer(Uid.this, SYNC, null, mOnBatteryTimeBase); + mSyncStats.put(name, t); + } + } + return t; + } + + public StopwatchTimer getJobTimerLocked(String name) { + StopwatchTimer t = mJobStats.get(name); + if (t == null) { + final int N = mJobStats.size(); + if (N > MAX_WAKELOCKS_PER_UID) { + name = BATCHED_WAKELOCK_NAME; + t = mJobStats.get(name); + } + if (t == null) { + t = new StopwatchTimer(Uid.this, JOB, null, mOnBatteryTimeBase); + mJobStats.put(name, t); + } + } + return t; + } + public StopwatchTimer getWakeTimerLocked(String name, int type) { Wakelock wl = mWakelockStats.get(name); if (wl == null) { @@ -5737,6 +5891,34 @@ public final class BatteryStatsImpl extends BatteryStats { return t; } + public void noteStartSyncLocked(String name, long elapsedRealtimeMs) { + StopwatchTimer t = getSyncTimerLocked(name); + if (t != null) { + t.startRunningLocked(elapsedRealtimeMs); + } + } + + public void noteStopSyncLocked(String name, long elapsedRealtimeMs) { + StopwatchTimer t = getSyncTimerLocked(name); + if (t != null) { + t.stopRunningLocked(elapsedRealtimeMs); + } + } + + public void noteStartJobLocked(String name, long elapsedRealtimeMs) { + StopwatchTimer t = getJobTimerLocked(name); + if (t != null) { + t.startRunningLocked(elapsedRealtimeMs); + } + } + + public void noteStopJobLocked(String name, long elapsedRealtimeMs) { + StopwatchTimer t = getJobTimerLocked(name); + if (t != null) { + t.stopRunningLocked(elapsedRealtimeMs); + } + } + public void noteStartWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) { StopwatchTimer t = getWakeTimerLocked(name, type); if (t != null) { @@ -7170,7 +7352,7 @@ public final class BatteryStatsImpl extends BatteryStats { // the last run until samples in this run. if (mHistoryBaseTime > 0) { long oldnow = SystemClock.elapsedRealtime(); - mHistoryBaseTime = (mHistoryBaseTime - oldnow) + 60*1000; + mHistoryBaseTime = mHistoryBaseTime - oldnow + 1; if (DEBUG_HISTORY) { StringBuilder sb = new StringBuilder(128); sb.append("****************** ADJUSTED mHistoryBaseTime: "); @@ -7433,6 +7615,26 @@ public final class BatteryStatsImpl extends BatteryStats { } } + int NS = in.readInt(); + if (NS > 100) { + Slog.w(TAG, "File corrupt: too many syncs " + NS); + return; + } + for (int is = 0; is < NS; is++) { + String name = in.readString(); + u.getSyncTimerLocked(name).readSummaryFromParcelLocked(in); + } + + int NJ = in.readInt(); + if (NJ > 100) { + Slog.w(TAG, "File corrupt: too many job timers " + NJ); + return; + } + for (int ij = 0; ij < NJ; ij++) { + String name = in.readString(); + u.getJobTimerLocked(name).readSummaryFromParcelLocked(in); + } + int NP = in.readInt(); if (NP > 1000) { Slog.w(TAG, "File corrupt: too many sensors " + NP); @@ -7484,7 +7686,7 @@ public final class BatteryStatsImpl extends BatteryStats { String pkgName = in.readString(); Uid.Pkg p = u.getPackageStatsLocked(pkgName); p.mWakeups = p.mLoadedWakeups = in.readInt(); - final int NS = in.readInt(); + NS = in.readInt(); if (NS > 1000) { Slog.w(TAG, "File corrupt: too many services " + NS); return; @@ -7717,6 +7919,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + int NS = u.mSyncStats.size(); + out.writeInt(NS); + for (int is=0; is 0) { for (Map.Entry sent @@ -7786,7 +8002,7 @@ public final class BatteryStatsImpl extends BatteryStats { void readFromParcelLocked(Parcel in) { int magic = in.readInt(); if (magic != MAGIC) { - throw new ParcelFormatException("Bad magic number"); + throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic)); } readHistory(in, false); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8ef6dd61ff92e..79cb60e16d4a2 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1811,10 +1811,18 @@ public final class ActivityManagerService extends ActivityManagerNative break; } case SYSTEM_USER_START_MSG: { + mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, + Integer.toString(msg.arg1), msg.arg1); mSystemServiceManager.startUser(msg.arg1); break; } case SYSTEM_USER_CURRENT_MSG: { + mBatteryStatsService.noteEvent( + BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH, + Integer.toString(msg.arg2), msg.arg2); + mBatteryStatsService.noteEvent( + BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, + Integer.toString(msg.arg1), msg.arg1); mSystemServiceManager.switchUser(msg.arg1); break; } @@ -10168,7 +10176,11 @@ public final class ActivityManagerService extends ActivityManagerNative } if (goingCallback != null) goingCallback.run(); - + + mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, + Integer.toString(mCurrentUserId), mCurrentUserId); + mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, + Integer.toString(mCurrentUserId), mCurrentUserId); mSystemServiceManager.startUser(mCurrentUserId); synchronized (this) { @@ -12441,12 +12453,11 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" lastCachedPss="); pw.println(r.lastCachedPss); pw.print(prefix); pw.print(" "); - pw.print("keeping="); pw.print(r.keeping); - pw.print(" cached="); pw.print(r.cached); + pw.print("cached="); pw.print(r.cached); pw.print(" empty="); pw.print(r.empty); pw.print(" hasAboveClient="); pw.println(r.hasAboveClient); - if (!r.keeping) { + if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { if (r.lastWakeTime != 0) { long wtime; BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics(); @@ -15200,7 +15211,6 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjSeq = mAdjSeq; app.curRawAdj = app.maxAdj; app.foregroundActivities = false; - app.keeping = true; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT; // System processes can do UI, and when they do we want to have @@ -15224,7 +15234,6 @@ public final class ActivityManagerService extends ActivityManagerNative return (app.curAdj=app.maxAdj); } - app.keeping = false; app.systemNoUi = false; // Determine the importance of the process, starting with most @@ -15469,9 +15478,6 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "cch-started-services"; } } - // Don't kill this process because it is doing work; it - // has said it is doing work. - app.keeping = true; } for (int conni = s.connections.size()-1; conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ @@ -15561,9 +15567,6 @@ public final class ActivityManagerService extends ActivityManagerNative if (!client.cached) { app.cached = false; } - if (client.keeping) { - app.keeping = true; - } adjType = "service"; } } @@ -15675,7 +15678,6 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "provider"; } app.cached &= client.cached; - app.keeping |= client.keeping; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; app.adjSource = client; @@ -15719,7 +15721,6 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; app.cached = false; - app.keeping = true; app.adjType = "provider"; app.adjTarget = cpr.name; } @@ -15802,9 +15803,6 @@ public final class ActivityManagerService extends ActivityManagerNative schedGroup = Process.THREAD_GROUP_DEFAULT; } } - if (adj < ProcessList.CACHED_APP_MIN_ADJ) { - app.keeping = true; - } // Do final modification to adj. Everything we do between here and applying // the final setAdj must be done in this function, because we will also use @@ -16026,7 +16024,7 @@ public final class ActivityManagerService extends ActivityManagerNative while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); - if (!app.keeping) { + if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { long wtime; synchronized (stats) { wtime = stats.getProcessWakeTime(app.info.uid, @@ -16071,7 +16069,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " during " + realtimeSince); app.baseProcessTracker.reportExcessiveWake(app.pkgList); } else if (doCpuKills && uptimeSince > 0 - && ((cputimeUsed*100)/uptimeSince) >= 50) { + && ((cputimeUsed*100)/uptimeSince) >= 25) { synchronized (stats) { stats.reportExcessiveCpuLocked(app.info.uid, app.processName, uptimeSince, cputimeUsed); @@ -16087,23 +16085,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping, + private final boolean applyOomAdjLocked(ProcessRecord app, ProcessRecord TOP_APP, boolean doingAll, long now) { boolean success = true; if (app.curRawAdj != app.setRawAdj) { - if (wasKeeping && !app.keeping) { - // This app is no longer something we want to keep. Note - // its current wake lock time to later know to kill it if - // it is not behaving well. - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, - app.pid, SystemClock.elapsedRealtime()); - } - app.lastCpuTime = app.curCpuTime; - } - app.setRawAdj = app.curRawAdj; } @@ -16192,6 +16178,21 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, "Proc state change of " + app.processName + " to " + app.curProcState); + boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE; + boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE; + if (setImportant && !curImportant) { + // This app is no longer something we consider important enough to allow to + // use arbitrary amounts of battery power. Note + // its current wake lock time to later know to kill it if + // it is not behaving well. + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, + app.pid, SystemClock.elapsedRealtime()); + } + app.lastCpuTime = app.curCpuTime; + + } app.setProcState = app.curProcState; if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { app.notCachedSinceIdle = false; @@ -16268,11 +16269,9 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - final boolean wasKeeping = app.keeping; - computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); - return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, now); + return applyOomAdjLocked(app, TOP_APP, doingAll, now); } final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground, @@ -16428,7 +16427,6 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord app = mLruProcesses.get(i); if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; - final boolean wasKeeping = app.keeping; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); // If we haven't yet assigned the final cached adj @@ -16483,7 +16481,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - applyOomAdjLocked(app, wasKeeping, TOP_APP, true, now); + applyOomAdjLocked(app, TOP_APP, true, now); // Count the number of process types. switch (app.curProcState) { @@ -17127,7 +17125,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (foreground) { - mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId)); + mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId, + oldUserId)); mHandler.removeMessages(REPORT_USER_SWITCH_MSG); mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, @@ -17502,6 +17501,9 @@ public final class ActivityManagerService extends ActivityManagerNative } uss.mState = UserStartedState.STATE_SHUTDOWN; } + mBatteryStatsService.noteEvent( + BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH, + Integer.toString(userId), userId); mSystemServiceManager.stopUser(userId); broadcastIntentLocked(null, null, shutdownIntent, null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE, diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index da444f9beca93..ac19bde081ec7 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -186,6 +186,34 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + public void noteSyncStart(String name, int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteSyncStartLocked(name, uid); + } + } + + public void noteSyncFinish(String name, int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteSyncFinishLocked(name, uid); + } + } + + public void noteJobStart(String name, int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteJobStartLocked(name, uid); + } + } + + public void noteJobFinish(String name, int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteJobFinishLocked(name, uid); + } + } + public void noteStartWakelock(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging) { enforceCallingPermission(); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 2f25bd495975f..a20be73f2722d 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -83,7 +83,6 @@ final class ProcessRecord { int pssProcState = -1; // The proc state we are currently requesting pss for boolean serviceb; // Process currently is on the service B list boolean serviceHighRam; // We are forcing to service B list due to its RAM use - boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle? boolean hasClientActivities; // Are there any client services with activities? @@ -225,8 +224,7 @@ final class ProcessRecord { pw.print(" lruSeq="); pw.print(lruSeq); pw.print(" lastPss="); pw.print(lastPss); pw.print(" lastCachedPss="); pw.println(lastCachedPss); - pw.print(prefix); pw.print("keeping="); pw.print(keeping); - pw.print(" cached="); pw.print(cached); + pw.print(prefix); pw.print("cached="); pw.print(cached); pw.print(" empty="); pw.println(empty); if (serviceb) { pw.print(prefix); pw.print("serviceb="); pw.print(serviceb); @@ -275,16 +273,15 @@ final class ProcessRecord { if (hasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices); } - if (!keeping) { + if (setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { long wtime; synchronized (mBatteryStats) { wtime = mBatteryStats.getProcessWakeTime(info.uid, pid, SystemClock.elapsedRealtime()); } - long timeUsed = wtime - lastWakeTime; pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime); pw.print(" timeUsed="); - TimeUtils.formatDuration(timeUsed, pw); pw.println(""); + TimeUtils.formatDuration(wtime-lastWakeTime, pw); pw.println(""); pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime); pw.print(" timeUsed="); TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println(""); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 1b40cdf5caf2e..08d6fc9d7a9f0 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -1212,8 +1212,7 @@ public class SyncManager { } else { try { mEventName = mSyncOperation.wakeLockName(); - mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_START, - mEventName, mSyncAdapterUid); + mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid); } catch (RemoteException e) { } } @@ -1232,8 +1231,7 @@ public class SyncManager { mBound = false; mContext.unbindService(this); try { - mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_FINISH, - mEventName, mSyncAdapterUid); + mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid); } catch (RemoteException e) { } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 7f8b23233e293..caae9f5116ee9 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -35,16 +35,20 @@ import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.app.IBatteryStats; import com.android.server.job.controllers.BatteryController; import com.android.server.job.controllers.ConnectivityController; import com.android.server.job.controllers.IdleController; @@ -52,8 +56,6 @@ import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; import com.android.server.job.controllers.TimeController; -import java.util.LinkedList; - /** * Responsible for taking jobs representing work to be performed by a client app, and determining * based on the criteria specified when that job should be run against the client application's @@ -74,7 +76,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final int MAX_JOB_CONTEXTS_COUNT = 3; static final String TAG = "JobManagerService"; /** Master list of jobs. */ - private final JobStore mJobs; + final JobStore mJobs; static final int MSG_JOB_EXPIRED = 0; static final int MSG_CHECK_JOB = 1; @@ -84,33 +86,41 @@ public class JobSchedulerService extends com.android.server.SystemService * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things * early. */ - private static final int MIN_IDLE_COUNT = 1; + static final int MIN_IDLE_COUNT = 1; /** * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule * things early. */ - private static final int MIN_CONNECTIVITY_COUNT = 2; + static final int MIN_CONNECTIVITY_COUNT = 2; /** * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running * some work early. */ - private static final int MIN_READY_JOBS_COUNT = 4; + static final int MIN_READY_JOBS_COUNT = 4; /** * Track Services that have currently active or pending jobs. The index is provided by * {@link JobStatus#getServiceToken()} */ - private final List mActiveServices = new LinkedList(); + final List mActiveServices = new ArrayList(); /** List of controllers that will notify this service of updates to jobs. */ - private List mControllers; + List mControllers; /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list * when ready to execute them. */ - private final LinkedList mPendingJobs = new LinkedList(); + final ArrayList mPendingJobs = new ArrayList(); + + final JobHandler mHandler; + final JobSchedulerStub mJobSchedulerStub; + + IBatteryStats mBatteryStats; + + /** + * Set to true once we are allowed to run third party apps. + */ + boolean mReadyToRock; - private final JobHandler mHandler; - private final JobSchedulerStub mJobSchedulerStub; /** * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we * still clean up. On reinstall the package will have a new uid. @@ -152,7 +162,9 @@ public class JobSchedulerService extends com.android.server.SystemService public List getPendingJobs(int uid) { ArrayList outList = new ArrayList(); synchronized (mJobs) { - for (JobStatus job : mJobs.getJobs()) { + ArraySet jobs = mJobs.getJobs(); + for (int i=0; i jobsForUser = mJobs.getJobsByUser(userHandle); - for (JobStatus toRemove : jobsForUser) { + for (int i=0; i jobsForUid = mJobs.getJobsByUid(uid); - for (JobStatus toRemove : jobsForUid) { + for (int i=0; i(); + mControllers = new ArrayList(); mControllers.add(ConnectivityController.get(this)); mControllers.add(TimeController.get(this)); mControllers.add(IdleController.get(this)); @@ -238,11 +252,6 @@ public class JobSchedulerService extends com.android.server.SystemService mHandler = new JobHandler(context.getMainLooper()); mJobSchedulerStub = new JobSchedulerStub(); - // Create the "runners". - for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { - mActiveServices.add( - new JobServiceContext(this, context.getMainLooper())); - } mJobs = JobStore.initAndGet(this); } @@ -262,6 +271,29 @@ public class JobSchedulerService extends com.android.server.SystemService final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); + } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + synchronized (mJobs) { + // Let's go! + mReadyToRock = true; + mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( + BatteryStats.SERVICE_NAME)); + // Create the "runners". + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { + mActiveServices.add( + new JobServiceContext(this, mBatteryStats, + getContext().getMainLooper())); + } + // Attach jobs to their controllers. + ArraySet jobs = mJobs.getJobs(); + for (int i=0; i jobs) { synchronized (mJobs) { - for (JobStatus js : jobs) { + for (int i=0; i jobs = mJobs.getJobs(); + for (int i=0; i runnableJobs = new ArrayList(); - for (JobStatus job : mJobs.getJobs()) { + ArraySet jobs = mJobs.getJobs(); + for (int i=0; i 0) { backoffCount++; @@ -539,8 +591,8 @@ public class JobSchedulerService extends com.android.server.SystemService if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT || runnableJobs.size() >= MIN_READY_JOBS_COUNT) { - for (JobStatus job : runnableJobs) { - mPendingJobs.add(job); + for (int i=0; i 0) { - for (JobStatus job : mJobs.getJobs()) { + ArraySet jobs = mJobs.getJobs(); + for (int i=0; i=0; i--) { + JobStatus ts = mJobSet.valueAt(i); if (ts.getUid() == uId && ts.getJobId() == jobId) { return true; } @@ -267,7 +268,8 @@ public class JobStore { List mStoreCopy = new ArrayList(); synchronized (JobStore.this) { // Copy over the jobs so we can release the lock before writing. - for (JobStatus jobStatus : mJobSet) { + for (int i=0; i