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 363 .getReasonCodeDescription(pe.stopReasons.keyAt(k))); 364 } 365 pw.println(); 366 } 367 } 368 } 369 pw.print(prefix); pw.print(" Max concurrency: "); 370 pw.print(mMaxTotalActive); pw.print(" total, "); 371 pw.print(mMaxFgActive); pw.println(" foreground"); 372 } 373 printPackageEntryState(ProtoOutputStream proto, long fieldId, long duration, int count)374 private void printPackageEntryState(ProtoOutputStream proto, long fieldId, 375 long duration, int count) { 376 final long token = proto.start(fieldId); 377 proto.write(DataSetProto.PackageEntryProto.State.DURATION_MS, duration); 378 proto.write(DataSetProto.PackageEntryProto.State.COUNT, count); 379 proto.end(token); 380 } 381 dump(ProtoOutputStream proto, long fieldId, long now, long nowElapsed, int filterUid)382 void dump(ProtoOutputStream proto, long fieldId, long now, long nowElapsed, int filterUid) { 383 final long token = proto.start(fieldId); 384 final long period = getTotalTime(now); 385 386 proto.write(DataSetProto.START_CLOCK_TIME_MS, mStartClockTime); 387 proto.write(DataSetProto.ELAPSED_TIME_MS, nowElapsed - mStartElapsedTime); 388 proto.write(DataSetProto.PERIOD_MS, period); 389 390 final int NE = mEntries.size(); 391 for (int i = 0; i < NE; i++) { 392 int uid = mEntries.keyAt(i); 393 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 394 continue; 395 } 396 ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); 397 final int NP = uidMap.size(); 398 for (int j = 0; j < NP; j++) { 399 final long peToken = proto.start(DataSetProto.PACKAGE_ENTRIES); 400 PackageEntry pe = uidMap.valueAt(j); 401 402 proto.write(DataSetProto.PackageEntryProto.UID, uid); 403 proto.write(DataSetProto.PackageEntryProto.PACKAGE_NAME, uidMap.keyAt(j)); 404 405 printPackageEntryState(proto, DataSetProto.PackageEntryProto.PENDING_STATE, 406 pe.getPendingTime(now), pe.pendingCount); 407 printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_STATE, 408 pe.getActiveTime(now), pe.activeCount); 409 printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_TOP_STATE, 410 pe.getActiveTopTime(now), pe.activeTopCount); 411 412 proto.write(DataSetProto.PackageEntryProto.PENDING, 413 pe.pendingNesting > 0 || pe.hadPending); 414 proto.write(DataSetProto.PackageEntryProto.ACTIVE, 415 pe.activeNesting > 0 || pe.hadActive); 416 proto.write(DataSetProto.PackageEntryProto.ACTIVE_TOP, 417 pe.activeTopNesting > 0 || pe.hadActiveTop); 418 419 for (int k = 0; k < pe.stopReasons.size(); k++) { 420 final long srcToken = 421 proto.start(DataSetProto.PackageEntryProto.STOP_REASONS); 422 423 proto.write(DataSetProto.PackageEntryProto.StopReasonCount.REASON, 424 pe.stopReasons.keyAt(k)); 425 proto.write(DataSetProto.PackageEntryProto.StopReasonCount.COUNT, 426 pe.stopReasons.valueAt(k)); 427 428 proto.end(srcToken); 429 } 430 431 proto.end(peToken); 432 } 433 } 434 435 proto.write(DataSetProto.MAX_CONCURRENCY, mMaxTotalActive); 436 proto.write(DataSetProto.MAX_FOREGROUND_CONCURRENCY, mMaxFgActive); 437 438 proto.end(token); 439 } 440 } 441 rebatchIfNeeded(long now)442 void rebatchIfNeeded(long now) { 443 long totalTime = mCurDataSet.getTotalTime(now); 444 if (totalTime > BATCHING_TIME) { 445 DataSet last = mCurDataSet; 446 last.mSummedTime = totalTime; 447 mCurDataSet = new DataSet(); 448 last.finish(mCurDataSet, now); 449 System.arraycopy(mLastDataSets, 0, mLastDataSets, 1, mLastDataSets.length-1); 450 mLastDataSets[0] = last; 451 } 452 } 453 notePending(JobStatus job)454 public void notePending(JobStatus job) { 455 final long now = sUptimeMillisClock.millis(); 456 job.madePending = now; 457 rebatchIfNeeded(now); 458 mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now); 459 } 460 noteNonpending(JobStatus job)461 public void noteNonpending(JobStatus job) { 462 final long now = sUptimeMillisClock.millis(); 463 mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now); 464 rebatchIfNeeded(now); 465 } 466 noteActive(JobStatus job)467 public void noteActive(JobStatus job) { 468 final long now = sUptimeMillisClock.millis(); 469 job.madeActive = now; 470 rebatchIfNeeded(now); 471 if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 472 mCurDataSet.incActiveTop(job.getSourceUid(), job.getSourcePackageName(), now); 473 } else { 474 mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now); 475 } 476 addEvent(job.getJob().isPeriodic() ? EVENT_START_PERIODIC_JOB : EVENT_START_JOB, 477 job.getSourceUid(), job.getBatteryName(), job.getJobId(), 0, null); 478 } 479 noteInactive(JobStatus job, int stopReason, String debugReason)480 public void noteInactive(JobStatus job, int stopReason, String debugReason) { 481 final long now = sUptimeMillisClock.millis(); 482 if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 483 mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now, 484 stopReason); 485 } else { 486 mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now, stopReason); 487 } 488 rebatchIfNeeded(now); 489 addEvent(job.getJob().isPeriodic() ? EVENT_STOP_JOB : EVENT_STOP_PERIODIC_JOB, 490 job.getSourceUid(), job.getBatteryName(), job.getJobId(), stopReason, debugReason); 491 } 492 noteConcurrency(int totalActive, int fgActive)493 public void noteConcurrency(int totalActive, int fgActive) { 494 if (totalActive > mCurDataSet.mMaxTotalActive) { 495 mCurDataSet.mMaxTotalActive = totalActive; 496 } 497 if (fgActive > mCurDataSet.mMaxFgActive) { 498 mCurDataSet.mMaxFgActive = fgActive; 499 } 500 } 501 getLoadFactor(JobStatus job)502 public float getLoadFactor(JobStatus job) { 503 final int uid = job.getSourceUid(); 504 final String pkg = job.getSourcePackageName(); 505 PackageEntry cur = mCurDataSet.getEntry(uid, pkg); 506 PackageEntry last = mLastDataSets[0] != null ? mLastDataSets[0].getEntry(uid, pkg) : null; 507 if (cur == null && last == null) { 508 return 0; 509 } 510 final long now = sUptimeMillisClock.millis(); 511 long time = 0; 512 if (cur != null) { 513 time += cur.getActiveTime(now) + cur.getPendingTime(now); 514 } 515 long period = mCurDataSet.getTotalTime(now); 516 if (last != null) { 517 time += last.getActiveTime(now) + last.getPendingTime(now); 518 period += mLastDataSets[0].getTotalTime(now); 519 } 520 return time / (float)period; 521 } 522 dump(PrintWriter pw, String prefix, int filterUid)523 public void dump(PrintWriter pw, String prefix, int filterUid) { 524 final long now = sUptimeMillisClock.millis(); 525 final long nowElapsed = sElapsedRealtimeClock.millis(); 526 final DataSet total; 527 if (mLastDataSets[0] != null) { 528 total = new DataSet(mLastDataSets[0]); 529 mLastDataSets[0].addTo(total, now); 530 } else { 531 total = new DataSet(mCurDataSet); 532 } 533 mCurDataSet.addTo(total, now); 534 for (int i = 1; i < mLastDataSets.length; i++) { 535 if (mLastDataSets[i] != null) { 536 mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowElapsed, filterUid); 537 pw.println(); 538 } 539 } 540 total.dump(pw, "Current stats", prefix, now, nowElapsed, filterUid); 541 } 542 dump(ProtoOutputStream proto, long fieldId, int filterUid)543 public void dump(ProtoOutputStream proto, long fieldId, int filterUid) { 544 final long token = proto.start(fieldId); 545 final long now = sUptimeMillisClock.millis(); 546 final long nowElapsed = sElapsedRealtimeClock.millis(); 547 548 final DataSet total; 549 if (mLastDataSets[0] != null) { 550 total = new DataSet(mLastDataSets[0]); 551 mLastDataSets[0].addTo(total, now); 552 } else { 553 total = new DataSet(mCurDataSet); 554 } 555 mCurDataSet.addTo(total, now); 556 557 for (int i = 1; i < mLastDataSets.length; i++) { 558 if (mLastDataSets[i] != null) { 559 mLastDataSets[i].dump(proto, JobPackageTrackerDumpProto.HISTORICAL_STATS, 560 now, nowElapsed, filterUid); 561 } 562 } 563 total.dump(proto, JobPackageTrackerDumpProto.CURRENT_STATS, 564 now, nowElapsed, filterUid); 565 566 proto.end(token); 567 } 568 dumpHistory(PrintWriter pw, String prefix, int filterUid)569 public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) { 570 final int size = mEventIndices.size(); 571 if (size <= 0) { 572 return false; 573 } 574 pw.println(" Job history:"); 575 final long now = sElapsedRealtimeClock.millis(); 576 for (int i=0; i<size; i++) { 577 final int index = mEventIndices.indexOf(i); 578 final int uid = mEventUids[index]; 579 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 580 continue; 581 } 582 final int cmd = mEventCmds[index] & EVENT_CMD_MASK; 583 if (cmd == EVENT_NULL) { 584 continue; 585 } 586 final String label; 587 switch (cmd) { 588 case EVENT_START_JOB: label = " START"; break; 589 case EVENT_STOP_JOB: label = " STOP"; break; 590 case EVENT_START_PERIODIC_JOB: label = "START-P"; break; 591 case EVENT_STOP_PERIODIC_JOB: label = " STOP-P"; break; 592 default: label = " ??"; break; 593 } 594 pw.print(prefix); 595 TimeUtils.formatDuration(mEventTimes[index]-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN); 596 pw.print(" "); 597 pw.print(label); 598 pw.print(": #"); 599 UserHandle.formatUid(pw, uid); 600 pw.print("/"); 601 pw.print(mEventJobIds[index]); 602 pw.print(" "); 603 pw.print(mEventTags[index]); 604 if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) { 605 pw.print(" "); 606 final String reason = mEventReasons[index]; 607 if (reason != null) { 608 pw.print(mEventReasons[index]); 609 } else { 610 pw.print(JobParameters.getReasonCodeDescription( 611 (mEventCmds[index] & EVENT_STOP_REASON_MASK) 612 >> EVENT_STOP_REASON_SHIFT)); 613 } 614 } 615 pw.println(); 616 } 617 return true; 618 } 619 dumpHistory(ProtoOutputStream proto, long fieldId, int filterUid)620 public void dumpHistory(ProtoOutputStream proto, long fieldId, int filterUid) { 621 final int size = mEventIndices.size(); 622 if (size == 0) { 623 return; 624 } 625 final long token = proto.start(fieldId); 626 627 final long now = sElapsedRealtimeClock.millis(); 628 for (int i = 0; i < size; i++) { 629 final int index = mEventIndices.indexOf(i); 630 final int uid = mEventUids[index]; 631 if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { 632 continue; 633 } 634 final int cmd = mEventCmds[index] & EVENT_CMD_MASK; 635 if (cmd == EVENT_NULL) { 636 continue; 637 } 638 final long heToken = proto.start(JobPackageHistoryProto.HISTORY_EVENT); 639 640 proto.write(JobPackageHistoryProto.HistoryEvent.EVENT, cmd); 641 proto.write(JobPackageHistoryProto.HistoryEvent.TIME_SINCE_EVENT_MS, now - mEventTimes[index]); 642 proto.write(JobPackageHistoryProto.HistoryEvent.UID, uid); 643 proto.write(JobPackageHistoryProto.HistoryEvent.JOB_ID, mEventJobIds[index]); 644 proto.write(JobPackageHistoryProto.HistoryEvent.TAG, mEventTags[index]); 645 if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) { 646 proto.write(JobPackageHistoryProto.HistoryEvent.STOP_REASON, 647 (mEventCmds[index] & EVENT_STOP_REASON_MASK) >> EVENT_STOP_REASON_SHIFT); 648 } 649 650 proto.end(heToken); 651 } 652 653 proto.end(token); 654 } 655 } 656