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