1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.job; 18 19 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 20 import static com.android.server.job.JobSchedulerService.sSystemClock; 21 import static com.android.server.job.JobSchedulerService.sUptimeMillisClock; 22 23 import android.app.job.JobInfo; 24 import android.app.job.JobParameters; 25 import android.os.UserHandle; 26 import android.text.format.DateFormat; 27 import android.util.ArrayMap; 28 import android.util.SparseArray; 29 import android.util.SparseIntArray; 30 import android.util.TimeUtils; 31 import android.util.proto.ProtoOutputStream; 32 33 import com.android.internal.util.RingBufferIndices; 34 import com.android.server.job.controllers.JobStatus; 35 36 import java.io.PrintWriter; 37 38 public final class JobPackageTracker { 39 // We batch every 30 minutes. 40 static final long BATCHING_TIME = 30*60*1000; 41 // Number of historical data sets we keep. 42 static final int NUM_HISTORY = 5; 43 44 private static final int EVENT_BUFFER_SIZE = 100; 45 46 public static final int EVENT_CMD_MASK = 0xff; 47 public static final int EVENT_STOP_REASON_SHIFT = 8; 48 public static final int EVENT_STOP_REASON_MASK = 0xff << EVENT_STOP_REASON_SHIFT; 49 public static final int EVENT_NULL = 0; 50 public static final int EVENT_START_JOB = 1; 51 public static final int EVENT_STOP_JOB = 2; 52 public static final int EVENT_START_PERIODIC_JOB = 3; 53 public static final int EVENT_STOP_PERIODIC_JOB = 4; 54 55 private final RingBufferIndices mEventIndices = new RingBufferIndices(EVENT_BUFFER_SIZE); 56 private final int[] mEventCmds = new int[EVENT_BUFFER_SIZE]; 57 private final long[] mEventTimes = new long[EVENT_BUFFER_SIZE]; 58 private final int[] mEventUids = new int[EVENT_BUFFER_SIZE]; 59 private final String[] mEventTags = new String[EVENT_BUFFER_SIZE]; 60 private final int[] mEventJobIds = new int[EVENT_BUFFER_SIZE]; 61 private final String[] mEventReasons = new String[EVENT_BUFFER_SIZE]; 62 addEvent(int cmd, int uid, String tag, int jobId, int stopReason, String debugReason)63 public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason, 64 String debugReason) { 65 int index = mEventIndices.add(); 66 mEventCmds[index] = cmd | ((stopReason<<EVENT_STOP_REASON_SHIFT) & EVENT_STOP_REASON_MASK); 67 mEventTimes[index] = sElapsedRealtimeClock.millis(); 68 mEventUids[index] = uid; 69 mEventTags[index] = tag; 70 mEventJobIds[index] = jobId; 71 mEventReasons[index] = debugReason; 72 } 73 74 DataSet mCurDataSet = new DataSet(); 75 DataSet[] mLastDataSets = new DataSet[NUM_HISTORY]; 76 77 final static class PackageEntry { 78 long pastActiveTime; 79 long activeStartTime; 80 int activeNesting; 81 int activeCount; 82 boolean hadActive; 83 long pastActiveTopTime; 84 long activeTopStartTime; 85 int activeTopNesting; 86 int activeTopCount; 87 boolean hadActiveTop; 88 long pastPendingTime; 89 long pendingStartTime; 90 int pendingNesting; 91 int pendingCount; 92 boolean hadPending; 93 final SparseIntArray stopReasons = new SparseIntArray(); 94 getActiveTime(long now)95 public long getActiveTime(long now) { 96 long time = pastActiveTime; 97 if (activeNesting > 0) { 98 time += now - activeStartTime; 99 } 100 return time; 101 } 102 getActiveTopTime(long now)103 public long getActiveTopTime(long now) { 104 long time = pastActiveTopTime; 105 if (activeTopNesting > 0) { 106 time += now - activeTopStartTime; 107 } 108 return time; 109 } 110 getPendingTime(long now)111 public long getPendingTime(long now) { 112 long time = pastPendingTime; 113 if (pendingNesting > 0) { 114 time += now - pendingStartTime; 115 } 116 return time; 117 } 118 } 119 120 final static class DataSet { 121 final SparseArray<ArrayMap<String, PackageEntry>> mEntries = new SparseArray<>(); 122 final long mStartUptimeTime; 123 final long mStartElapsedTime; 124 final long mStartClockTime; 125 long mSummedTime; 126 int mMaxTotalActive; 127 int mMaxFgActive; 128 DataSet(DataSet otherTimes)129 public DataSet(DataSet otherTimes) { 130 mStartUptimeTime = otherTimes.mStartUptimeTime; 131 mStartElapsedTime = otherTimes.mStartElapsedTime; 132 mStartClockTime = otherTimes.mStartClockTime; 133 } 134 DataSet()135 public DataSet() { 136 mStartUptimeTime = sUptimeMillisClock.millis(); 137 mStartElapsedTime = sElapsedRealtimeClock.millis(); 138 mStartClockTime = sSystemClock.millis(); 139 } 140 getOrCreateEntry(int uid, String pkg)141 private PackageEntry getOrCreateEntry(int uid, String pkg) { 142 ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid); 143 if (uidMap == null) { 144 uidMap = new ArrayMap<>(); 145 mEntries.put(uid, uidMap); 146 } 147 PackageEntry entry = uidMap.get(pkg); 148 if (entry == null) { 149 entry = new PackageEntry(); 150 uidMap.put(pkg, entry); 151 } 152 return entry; 153 } 154 getEntry(int uid, String pkg)155 public PackageEntry getEntry(int uid, String pkg) { 156 ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid); 157 if (uidMap == null) { 158 return null; 159 } 160 return uidMap.get(pkg); 161 } 162 getTotalTime(long now)163 long getTotalTime(long now) { 164 if (mSummedTime > 0) { 165 return mSummedTime; 166 } 167 return now - mStartUptimeTime; 168 } 169 incPending(int uid, String pkg, long now)170 void incPending(int uid, String pkg, long now) { 171 PackageEntry pe = getOrCreateEntry(uid, pkg); 172 if (pe.pendingNesting == 0) { 173 pe.pendingStartTime = now; 174 pe.pendingCount++; 175 } 176 pe.pendingNesting++; 177 } 178 decPending(int uid, String pkg, long now)179 void decPending(int uid, String pkg, long now) { 180 PackageEntry pe = getOrCreateEntry(uid, pkg); 181 if (pe.pendingNesting == 1) { 182 pe.pastPendingTime += now - pe.pendingStartTime; 183 } 184 pe.pendingNesting--; 185 } 186 incActive(int uid, String pkg, long now)187 void incActive(int uid, String pkg, long now) { 188 PackageEntry pe = getOrCreateEntry(uid, pkg); 189 if (pe.activeNesting == 0) { 190 pe.activeStartTime = now; 191 pe.activeCount++; 192 } 193 pe.activeNesting++; 194 } 195 decActive(int uid, String pkg, long now, int stopReason)196 void decActive(int uid, String pkg, long now, int stopReason) { 197 PackageEntry pe = getOrCreateEntry(uid, pkg); 198 if (pe.activeNesting == 1) { 199 pe.pastActiveTime += now - pe.activeStartTime; 200 } 201 pe.activeNesting--; 202 int count = pe.stopReasons.get(stopReason, 0); 203 pe.stopReasons.put(stopReason, count+1); 204 } 205 incActiveTop(int uid, String pkg, long now)206 void incActiveTop(int uid, String pkg, long now) { 207 PackageEntry pe = getOrCreateEntry(uid, pkg); 208 if (pe.activeTopNesting == 0) { 209 pe.activeTopStartTime = now; 210 pe.activeTopCount++; 211 } 212 pe.activeTopNesting++; 213 } 214 decActiveTop(int uid, String pkg, long now, int stopReason)215 void decActiveTop(int uid, String pkg, long now, int stopReason) { 216 PackageEntry pe = getOrCreateEntry(uid, pkg); 217 if (pe.activeTopNesting == 1) { 218 pe.pastActiveTopTime += now - pe.activeTopStartTime; 219 } 220 pe.activeTopNesting--; 221 int count = pe.stopReasons.get(stopReason, 0); 222 pe.stopReasons.put(stopReason, count+1); 223 } 224 finish(DataSet next, long now)225 void finish(DataSet next, long now) { 226 for (int i = mEntries.size() - 1; i >= 0; i--) { 227 ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); 228 for (int j = uidMap.size() - 1; j >= 0; j--) { 229 PackageEntry pe = uidMap.valueAt(j); 230 if (pe.activeNesting > 0 || pe.activeTopNesting > 0 || pe.pendingNesting > 0) { 231 // Propagate existing activity in to next data set. 232 PackageEntry nextPe = next.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j)); 233 nextPe.activeStartTime = now; 234 nextPe.activeNesting = pe.activeNesting; 235 nextPe.activeTopStartTime = now; 236 nextPe.activeTopNesting = pe.activeTopNesting; 237 nextPe.pendingStartTime = now; 238 nextPe.pendingNesting = pe.pendingNesting; 239 // Finish it off. 240 if (pe.activeNesting > 0) { 241 pe.pastActiveTime += now - pe.activeStartTime; 242 pe.activeNesting = 0; 243 } 244 if (pe.activeTopNesting > 0) { 245 pe.pastActiveTopTime += now - pe.activeTopStartTime; 246 pe.activeTopNesting = 0; 247 } 248 if (pe.pendingNesting > 0) { 249 pe.pastPendingTime += now - pe.pendingStartTime; 250 pe.pendingNesting = 0; 251 } 252 } 253 } 254 } 255 } 256 addTo(DataSet out, long now)257 void addTo(DataSet out, long now) { 258 out.mSummedTime += getTotalTime(now); 259 for (int i = mEntries.size() - 1; i >= 0; i--) { 260 ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); 261 for (int j = uidMap.size() - 1; j >= 0; j--) { 262 PackageEntry pe = uidMap.valueAt(j); 263 PackageEntry outPe = out.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j)); 264 outPe.pastActiveTime += pe.pastActiveTime; 265 outPe.activeCount += pe.activeCount; 266 outPe.pastActiveTopTime += pe.pastActiveTopTime; 267 outPe.activeTopCount += pe.activeTopCount; 268 outPe.pastPendingTime += pe.pastPendingTime; 269 outPe.pendingCount += pe.pendingCount; 270 if (pe.activeNesting > 0) { 271 outPe.pastActiveTime += now - pe.activeStartTime; 272 outPe.hadActive = true; 273 } 274 if (pe.activeTopNesting > 0) { 275 outPe.pastActiveTopTime += now - pe.activeTopStartTime; 276 outPe.hadActiveTop = true; 277 } 278 if (pe.pendingNesting > 0) { 279 outPe.pastPendingTime += now - pe.pendingStartTime; 280 outPe.hadPending = true; 281 } 282 for (int k = pe.stopReasons.size()-1; k >= 0; k--) { 283 int type = pe.stopReasons.keyAt(k); 284 outPe.stopReasons.put(type, outPe.stopReasons.get(type, 0) 285 + pe.stopReasons.valueAt(k)); 286 } 287 } 288 } 289 if (mMaxTotalActive > out.mMaxTotalActive) { 290 out.mMaxTotalActive = mMaxTotalActive; 291 } 292 if (mMaxFgActive > out.mMaxFgActive) { 293 out.mMaxFgActive = mMaxFgActive; 294 } 295 } 296 printDuration(PrintWriter pw, long period, long duration, int count, String suffix)297 void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) { 298 float fraction = duration / (float) period; 299 int percent = (int) ((fraction * 100) + .5f); 300 if (percent > 0) { 301 pw.print(" "); 302 pw.print(percent); 303 pw.print("% "); 304 pw.print(count); 305 pw.print("x "); 306 pw.print(suffix); 307 } else if (count > 0) { 308 pw.print(" "); 309 pw.print(count); 310 pw.print("x "); 311 pw.print(suffix); 312 } 313 } 314 dump(PrintWriter pw, String header, String prefix, long now, long nowElapsed, int filterUid)315 void dump(PrintWriter pw, String header, String prefix, long now, long nowElapsed, 316 int filterUid) { 317 final long period = getTotalTime(now); 318 pw.print(prefix); pw.print(header); pw.print(" at "); 319 pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString()); 320 pw.print(" ("); 321 TimeUtils.formatDuration(mStartElapsedTime, nowElapsed, pw); 322 pw.print(") over "); 323 TimeUtils.formatDuration(period, pw); 324 pw.println(":"); 325 final int NE = mEntries.size(); 326 for (int i = 0; i < NE; i++) { 327 int uid = mEntries.keyAt(i); 328 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 329 continue; 330 } 331 ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); 332 final int NP = uidMap.size(); 333 for (int j = 0; j < NP; j++) { 334 PackageEntry pe = uidMap.valueAt(j); 335 pw.print(prefix); pw.print(" "); 336 UserHandle.formatUid(pw, uid); 337 pw.print(" / "); pw.print(uidMap.keyAt(j)); 338 pw.println(":"); 339 pw.print(prefix); pw.print(" "); 340 printDuration(pw, period, pe.getPendingTime(now), pe.pendingCount, "pending"); 341 printDuration(pw, period, pe.getActiveTime(now), pe.activeCount, "active"); 342 printDuration(pw, period, pe.getActiveTopTime(now), pe.activeTopCount, 343 "active-top"); 344 if (pe.pendingNesting > 0 || pe.hadPending) { 345 pw.print(" (pending)"); 346 } 347 if (pe.activeNesting > 0 || pe.hadActive) { 348 pw.print(" (active)"); 349 } 350 if (pe.activeTopNesting > 0 || pe.hadActiveTop) { 351 pw.print(" (active-top)"); 352 } 353 pw.println(); 354 if (pe.stopReasons.size() > 0) { 355 pw.print(prefix); pw.print(" "); 356 for (int k = 0; k < pe.stopReasons.size(); k++) { 357 if (k > 0) { 358 pw.print(", "); 359 } 360 pw.print(pe.stopReasons.valueAt(k)); 361 pw.print("x "); 362 pw.print(JobParameters.getReasonName(pe.stopReasons.keyAt(k))); 363 } 364 pw.println(); 365 } 366 } 367 } 368 pw.print(prefix); pw.print(" Max concurrency: "); 369 pw.print(mMaxTotalActive); pw.print(" total, "); 370 pw.print(mMaxFgActive); pw.println(" foreground"); 371 } 372 printPackageEntryState(ProtoOutputStream proto, long fieldId, long duration, int count)373 private void printPackageEntryState(ProtoOutputStream proto, long fieldId, 374 long duration, int count) { 375 final long token = proto.start(fieldId); 376 proto.write(DataSetProto.PackageEntryProto.State.DURATION_MS, duration); 377 proto.write(DataSetProto.PackageEntryProto.State.COUNT, count); 378 proto.end(token); 379 } 380 dump(ProtoOutputStream proto, long fieldId, long now, long nowElapsed, int filterUid)381 void dump(ProtoOutputStream proto, long fieldId, long now, long nowElapsed, int filterUid) { 382 final long token = proto.start(fieldId); 383 final long period = getTotalTime(now); 384 385 proto.write(DataSetProto.START_CLOCK_TIME_MS, mStartClockTime); 386 proto.write(DataSetProto.ELAPSED_TIME_MS, nowElapsed - mStartElapsedTime); 387 proto.write(DataSetProto.PERIOD_MS, period); 388 389 final int NE = mEntries.size(); 390 for (int i = 0; i < NE; i++) { 391 int uid = mEntries.keyAt(i); 392 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 393 continue; 394 } 395 ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); 396 final int NP = uidMap.size(); 397 for (int j = 0; j < NP; j++) { 398 final long peToken = proto.start(DataSetProto.PACKAGE_ENTRIES); 399 PackageEntry pe = uidMap.valueAt(j); 400 401 proto.write(DataSetProto.PackageEntryProto.UID, uid); 402 proto.write(DataSetProto.PackageEntryProto.PACKAGE_NAME, uidMap.keyAt(j)); 403 404 printPackageEntryState(proto, DataSetProto.PackageEntryProto.PENDING_STATE, 405 pe.getPendingTime(now), pe.pendingCount); 406 printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_STATE, 407 pe.getActiveTime(now), pe.activeCount); 408 printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_TOP_STATE, 409 pe.getActiveTopTime(now), pe.activeTopCount); 410 411 proto.write(DataSetProto.PackageEntryProto.PENDING, 412 pe.pendingNesting > 0 || pe.hadPending); 413 proto.write(DataSetProto.PackageEntryProto.ACTIVE, 414 pe.activeNesting > 0 || pe.hadActive); 415 proto.write(DataSetProto.PackageEntryProto.ACTIVE_TOP, 416 pe.activeTopNesting > 0 || pe.hadActiveTop); 417 418 for (int k = 0; k < pe.stopReasons.size(); k++) { 419 final long srcToken = 420 proto.start(DataSetProto.PackageEntryProto.STOP_REASONS); 421 422 proto.write(DataSetProto.PackageEntryProto.StopReasonCount.REASON, 423 pe.stopReasons.keyAt(k)); 424 proto.write(DataSetProto.PackageEntryProto.StopReasonCount.COUNT, 425 pe.stopReasons.valueAt(k)); 426 427 proto.end(srcToken); 428 } 429 430 proto.end(peToken); 431 } 432 } 433 434 proto.write(DataSetProto.MAX_CONCURRENCY, mMaxTotalActive); 435 proto.write(DataSetProto.MAX_FOREGROUND_CONCURRENCY, mMaxFgActive); 436 437 proto.end(token); 438 } 439 } 440 rebatchIfNeeded(long now)441 void rebatchIfNeeded(long now) { 442 long totalTime = mCurDataSet.getTotalTime(now); 443 if (totalTime > BATCHING_TIME) { 444 DataSet last = mCurDataSet; 445 last.mSummedTime = totalTime; 446 mCurDataSet = new DataSet(); 447 last.finish(mCurDataSet, now); 448 System.arraycopy(mLastDataSets, 0, mLastDataSets, 1, mLastDataSets.length-1); 449 mLastDataSets[0] = last; 450 } 451 } 452 notePending(JobStatus job)453 public void notePending(JobStatus job) { 454 final long now = sUptimeMillisClock.millis(); 455 job.madePending = now; 456 rebatchIfNeeded(now); 457 mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now); 458 } 459 noteNonpending(JobStatus job)460 public void noteNonpending(JobStatus job) { 461 final long now = sUptimeMillisClock.millis(); 462 mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now); 463 rebatchIfNeeded(now); 464 } 465 noteActive(JobStatus job)466 public void noteActive(JobStatus job) { 467 final long now = sUptimeMillisClock.millis(); 468 job.madeActive = now; 469 rebatchIfNeeded(now); 470 if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 471 mCurDataSet.incActiveTop(job.getSourceUid(), job.getSourcePackageName(), now); 472 } else { 473 mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now); 474 } 475 addEvent(job.getJob().isPeriodic() ? EVENT_START_PERIODIC_JOB : EVENT_START_JOB, 476 job.getSourceUid(), job.getBatteryName(), job.getJobId(), 0, null); 477 } 478 noteInactive(JobStatus job, int stopReason, String debugReason)479 public void noteInactive(JobStatus job, int stopReason, String debugReason) { 480 final long now = sUptimeMillisClock.millis(); 481 if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 482 mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now, 483 stopReason); 484 } else { 485 mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now, stopReason); 486 } 487 rebatchIfNeeded(now); 488 addEvent(job.getJob().isPeriodic() ? EVENT_STOP_JOB : EVENT_STOP_PERIODIC_JOB, 489 job.getSourceUid(), job.getBatteryName(), job.getJobId(), stopReason, debugReason); 490 } 491 noteConcurrency(int totalActive, int fgActive)492 public void noteConcurrency(int totalActive, int fgActive) { 493 if (totalActive > mCurDataSet.mMaxTotalActive) { 494 mCurDataSet.mMaxTotalActive = totalActive; 495 } 496 if (fgActive > mCurDataSet.mMaxFgActive) { 497 mCurDataSet.mMaxFgActive = fgActive; 498 } 499 } 500 getLoadFactor(JobStatus job)501 public float getLoadFactor(JobStatus job) { 502 final int uid = job.getSourceUid(); 503 final String pkg = job.getSourcePackageName(); 504 PackageEntry cur = mCurDataSet.getEntry(uid, pkg); 505 PackageEntry last = mLastDataSets[0] != null ? mLastDataSets[0].getEntry(uid, pkg) : null; 506 if (cur == null && last == null) { 507 return 0; 508 } 509 final long now = sUptimeMillisClock.millis(); 510 long time = 0; 511 if (cur != null) { 512 time += cur.getActiveTime(now) + cur.getPendingTime(now); 513 } 514 long period = mCurDataSet.getTotalTime(now); 515 if (last != null) { 516 time += last.getActiveTime(now) + last.getPendingTime(now); 517 period += mLastDataSets[0].getTotalTime(now); 518 } 519 return time / (float)period; 520 } 521 dump(PrintWriter pw, String prefix, int filterUid)522 public void dump(PrintWriter pw, String prefix, int filterUid) { 523 final long now = sUptimeMillisClock.millis(); 524 final long nowElapsed = sElapsedRealtimeClock.millis(); 525 final DataSet total; 526 if (mLastDataSets[0] != null) { 527 total = new DataSet(mLastDataSets[0]); 528 mLastDataSets[0].addTo(total, now); 529 } else { 530 total = new DataSet(mCurDataSet); 531 } 532 mCurDataSet.addTo(total, now); 533 for (int i = 1; i < mLastDataSets.length; i++) { 534 if (mLastDataSets[i] != null) { 535 mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowElapsed, filterUid); 536 pw.println(); 537 } 538 } 539 total.dump(pw, "Current stats", prefix, now, nowElapsed, filterUid); 540 } 541 dump(ProtoOutputStream proto, long fieldId, int filterUid)542 public void dump(ProtoOutputStream proto, long fieldId, int filterUid) { 543 final long token = proto.start(fieldId); 544 final long now = sUptimeMillisClock.millis(); 545 final long nowElapsed = sElapsedRealtimeClock.millis(); 546 547 final DataSet total; 548 if (mLastDataSets[0] != null) { 549 total = new DataSet(mLastDataSets[0]); 550 mLastDataSets[0].addTo(total, now); 551 } else { 552 total = new DataSet(mCurDataSet); 553 } 554 mCurDataSet.addTo(total, now); 555 556 for (int i = 1; i < mLastDataSets.length; i++) { 557 if (mLastDataSets[i] != null) { 558 mLastDataSets[i].dump(proto, JobPackageTrackerDumpProto.HISTORICAL_STATS, 559 now, nowElapsed, filterUid); 560 } 561 } 562 total.dump(proto, JobPackageTrackerDumpProto.CURRENT_STATS, 563 now, nowElapsed, filterUid); 564 565 proto.end(token); 566 } 567 dumpHistory(PrintWriter pw, String prefix, int filterUid)568 public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) { 569 final int size = mEventIndices.size(); 570 if (size <= 0) { 571 return false; 572 } 573 pw.println(" Job history:"); 574 final long now = sElapsedRealtimeClock.millis(); 575 for (int i=0; i<size; i++) { 576 final int index = mEventIndices.indexOf(i); 577 final int uid = mEventUids[index]; 578 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 579 continue; 580 } 581 final int cmd = mEventCmds[index] & EVENT_CMD_MASK; 582 if (cmd == EVENT_NULL) { 583 continue; 584 } 585 final String label; 586 switch (cmd) { 587 case EVENT_START_JOB: label = " START"; break; 588 case EVENT_STOP_JOB: label = " STOP"; break; 589 case EVENT_START_PERIODIC_JOB: label = "START-P"; break; 590 case EVENT_STOP_PERIODIC_JOB: label = " STOP-P"; break; 591 default: label = " ??"; break; 592 } 593 pw.print(prefix); 594 TimeUtils.formatDuration(mEventTimes[index]-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN); 595 pw.print(" "); 596 pw.print(label); 597 pw.print(": #"); 598 UserHandle.formatUid(pw, uid); 599 pw.print("/"); 600 pw.print(mEventJobIds[index]); 601 pw.print(" "); 602 pw.print(mEventTags[index]); 603 if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) { 604 pw.print(" "); 605 final String reason = mEventReasons[index]; 606 if (reason != null) { 607 pw.print(mEventReasons[index]); 608 } else { 609 pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK) 610 >> EVENT_STOP_REASON_SHIFT)); 611 } 612 } 613 pw.println(); 614 } 615 return true; 616 } 617 dumpHistory(ProtoOutputStream proto, long fieldId, int filterUid)618 public void dumpHistory(ProtoOutputStream proto, long fieldId, int filterUid) { 619 final int size = mEventIndices.size(); 620 if (size == 0) { 621 return; 622 } 623 final long token = proto.start(fieldId); 624 625 final long now = sElapsedRealtimeClock.millis(); 626 for (int i = 0; i < size; i++) { 627 final int index = mEventIndices.indexOf(i); 628 final int uid = mEventUids[index]; 629 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 630 continue; 631 } 632 final int cmd = mEventCmds[index] & EVENT_CMD_MASK; 633 if (cmd == EVENT_NULL) { 634 continue; 635 } 636 final long heToken = proto.start(JobPackageHistoryProto.HISTORY_EVENT); 637 638 proto.write(JobPackageHistoryProto.HistoryEvent.EVENT, cmd); 639 proto.write(JobPackageHistoryProto.HistoryEvent.TIME_SINCE_EVENT_MS, now - mEventTimes[index]); 640 proto.write(JobPackageHistoryProto.HistoryEvent.UID, uid); 641 proto.write(JobPackageHistoryProto.HistoryEvent.JOB_ID, mEventJobIds[index]); 642 proto.write(JobPackageHistoryProto.HistoryEvent.TAG, mEventTags[index]); 643 if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) { 644 proto.write(JobPackageHistoryProto.HistoryEvent.STOP_REASON, 645 (mEventCmds[index] & EVENT_STOP_REASON_MASK) >> EVENT_STOP_REASON_SHIFT); 646 } 647 648 proto.end(heToken); 649 } 650 651 proto.end(token); 652 } 653 } 654