1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.fuelgauge; 16 17 import android.content.Context; 18 import android.content.Intent; 19 import android.content.IntentFilter; 20 import android.content.res.Resources; 21 import android.os.AsyncTask; 22 import android.os.BatteryManager; 23 import android.os.BatteryStats.HistoryItem; 24 import android.os.BatteryStatsManager; 25 import android.os.BatteryUsageStats; 26 import android.os.SystemClock; 27 import android.provider.Settings; 28 import android.text.format.Formatter; 29 import android.util.Log; 30 import android.util.SparseIntArray; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.annotation.WorkerThread; 35 36 import com.android.internal.os.BatteryStatsHistoryIterator; 37 import com.android.settings.Utils; 38 import com.android.settings.overlay.FeatureFactory; 39 import com.android.settings.widget.UsageView; 40 import com.android.settingslib.R; 41 import com.android.settingslib.fuelgauge.BatteryStatus; 42 import com.android.settingslib.fuelgauge.Estimate; 43 import com.android.settingslib.fuelgauge.EstimateKt; 44 import com.android.settingslib.utils.PowerUtil; 45 import com.android.settingslib.utils.StringUtil; 46 47 public class BatteryInfo { 48 private static final String TAG = "BatteryInfo"; 49 50 public CharSequence chargeLabel; 51 public CharSequence remainingLabel; 52 public int batteryLevel; 53 public int batteryStatus; 54 public int pluggedStatus; 55 public boolean discharging = true; 56 public boolean isBatteryDefender; 57 public boolean isFastCharging; 58 public long remainingTimeUs = 0; 59 public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN; 60 public String batteryPercentString; 61 public String statusLabel; 62 public String suggestionLabel; 63 private boolean mCharging; 64 private BatteryUsageStats mBatteryUsageStats; 65 private static final String LOG_TAG = "BatteryInfo"; 66 private long timePeriod; 67 68 public interface Callback { onBatteryInfoLoaded(BatteryInfo info)69 void onBatteryInfoLoaded(BatteryInfo info); 70 } 71 bindHistory(final UsageView view, BatteryDataParser... parsers)72 public void bindHistory(final UsageView view, BatteryDataParser... parsers) { 73 final Context context = view.getContext(); 74 BatteryDataParser parser = 75 new BatteryDataParser() { 76 SparseIntArray mPoints = new SparseIntArray(); 77 long mStartTime; 78 int mLastTime = -1; 79 byte mLastLevel; 80 81 @Override 82 public void onParsingStarted(long startTime, long endTime) { 83 this.mStartTime = startTime; 84 timePeriod = endTime - startTime; 85 view.clearPaths(); 86 // Initially configure the graph for history only. 87 view.configureGraph((int) timePeriod, 100); 88 } 89 90 @Override 91 public void onDataPoint(long time, HistoryItem record) { 92 mLastTime = (int) time; 93 mLastLevel = record.batteryLevel; 94 mPoints.put(mLastTime, mLastLevel); 95 } 96 97 @Override 98 public void onDataGap() { 99 if (mPoints.size() > 1) { 100 view.addPath(mPoints); 101 } 102 mPoints.clear(); 103 } 104 105 @Override 106 public void onParsingDone() { 107 onDataGap(); 108 109 // Add projection if we have an estimate. 110 if (remainingTimeUs != 0) { 111 PowerUsageFeatureProvider provider = 112 FeatureFactory.getFeatureFactory() 113 .getPowerUsageFeatureProvider(); 114 if (!mCharging 115 && provider.isEnhancedBatteryPredictionEnabled(context)) { 116 mPoints = 117 provider.getEnhancedBatteryPredictionCurve( 118 context, mStartTime); 119 } else { 120 // Linear extrapolation. 121 if (mLastTime >= 0) { 122 mPoints.put(mLastTime, mLastLevel); 123 mPoints.put( 124 (int) 125 (timePeriod 126 + PowerUtil.convertUsToMs( 127 remainingTimeUs)), 128 mCharging ? 100 : 0); 129 } 130 } 131 } 132 133 // If we have a projection, reconfigure the graph to show it. 134 if (mPoints != null && mPoints.size() > 0) { 135 int maxTime = mPoints.keyAt(mPoints.size() - 1); 136 view.configureGraph(maxTime, 100); 137 view.addProjectedPath(mPoints); 138 } 139 } 140 }; 141 BatteryDataParser[] parserList = new BatteryDataParser[parsers.length + 1]; 142 for (int i = 0; i < parsers.length; i++) { 143 parserList[i] = parsers[i]; 144 } 145 parserList[parsers.length] = parser; 146 parseBatteryHistory(parserList); 147 String timeString = 148 context.getString( 149 R.string.charge_length_format, 150 Formatter.formatShortElapsedTime(context, timePeriod)); 151 String remaining = ""; 152 if (remainingTimeUs != 0) { 153 remaining = 154 context.getString( 155 R.string.remaining_length_format, 156 Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000)); 157 } 158 view.setBottomLabels(new CharSequence[] {timeString, remaining}); 159 } 160 161 /** Gets battery info */ getBatteryInfo( final Context context, final Callback callback, boolean shortString)162 public static void getBatteryInfo( 163 final Context context, final Callback callback, boolean shortString) { 164 BatteryInfo.getBatteryInfo(context, callback, /* batteryUsageStats */ null, shortString); 165 } 166 getSettingsChargeTimeRemaining(final Context context)167 static long getSettingsChargeTimeRemaining(final Context context) { 168 return Settings.Global.getLong( 169 context.getContentResolver(), 170 com.android.settingslib.fuelgauge.BatteryUtils.GLOBAL_TIME_TO_FULL_MILLIS, 171 -1); 172 } 173 174 /** Gets battery info */ getBatteryInfo( final Context context, final Callback callback, @Nullable final BatteryUsageStats batteryUsageStats, boolean shortString)175 public static void getBatteryInfo( 176 final Context context, 177 final Callback callback, 178 @Nullable final BatteryUsageStats batteryUsageStats, 179 boolean shortString) { 180 new AsyncTask<Void, Void, BatteryInfo>() { 181 @Override 182 protected BatteryInfo doInBackground(Void... params) { 183 boolean shouldCloseBatteryUsageStats = false; 184 BatteryUsageStats stats; 185 if (batteryUsageStats != null) { 186 stats = batteryUsageStats; 187 } else { 188 try { 189 stats = 190 context.getSystemService(BatteryStatsManager.class) 191 .getBatteryUsageStats(); 192 shouldCloseBatteryUsageStats = true; 193 } catch (RuntimeException e) { 194 Log.e(TAG, "getBatteryInfo() from getBatteryUsageStats()", e); 195 // Use default BatteryUsageStats. 196 stats = new BatteryUsageStats.Builder(new String[0]).build(); 197 } 198 } 199 final BatteryInfo batteryInfo = getBatteryInfo(context, stats, shortString); 200 if (shouldCloseBatteryUsageStats) { 201 try { 202 stats.close(); 203 } catch (Exception e) { 204 Log.e(TAG, "BatteryUsageStats.close() failed", e); 205 } 206 } 207 return batteryInfo; 208 } 209 210 @Override 211 protected void onPostExecute(BatteryInfo batteryInfo) { 212 final long startTime = System.currentTimeMillis(); 213 callback.onBatteryInfoLoaded(batteryInfo); 214 BatteryUtils.logRuntime(LOG_TAG, "time for callback", startTime); 215 } 216 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 217 } 218 219 /** Creates a BatteryInfo based on BatteryUsageStats */ 220 @WorkerThread getBatteryInfo( final Context context, @NonNull final BatteryUsageStats batteryUsageStats, boolean shortString)221 public static BatteryInfo getBatteryInfo( 222 final Context context, 223 @NonNull final BatteryUsageStats batteryUsageStats, 224 boolean shortString) { 225 final long batteryStatsTime = System.currentTimeMillis(); 226 BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime); 227 228 final long startTime = System.currentTimeMillis(); 229 PowerUsageFeatureProvider provider = 230 FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); 231 final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); 232 233 final Intent batteryBroadcast = 234 context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 235 // 0 means we are discharging, anything else means charging 236 final boolean discharging = 237 batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0; 238 239 if (discharging && provider.isEnhancedBatteryPredictionEnabled(context)) { 240 Estimate estimate = provider.getEnhancedBatteryPrediction(context); 241 if (estimate != null) { 242 Estimate.storeCachedEstimate(context, estimate); 243 BatteryUtils.logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime); 244 return BatteryInfo.getBatteryInfo( 245 context, 246 batteryBroadcast, 247 batteryUsageStats, 248 estimate, 249 elapsedRealtimeUs, 250 shortString); 251 } 252 } 253 final long prediction = discharging ? batteryUsageStats.getBatteryTimeRemainingMs() : 0; 254 final Estimate estimate = 255 new Estimate( 256 prediction, 257 false, /* isBasedOnUsage */ 258 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 259 BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime); 260 return BatteryInfo.getBatteryInfo( 261 context, 262 batteryBroadcast, 263 batteryUsageStats, 264 estimate, 265 elapsedRealtimeUs, 266 shortString); 267 } 268 269 @WorkerThread getBatteryInfoOld( Context context, Intent batteryBroadcast, BatteryUsageStats batteryUsageStats, long elapsedRealtimeUs, boolean shortString)270 public static BatteryInfo getBatteryInfoOld( 271 Context context, 272 Intent batteryBroadcast, 273 BatteryUsageStats batteryUsageStats, 274 long elapsedRealtimeUs, 275 boolean shortString) { 276 Estimate estimate = 277 new Estimate( 278 batteryUsageStats.getBatteryTimeRemainingMs(), 279 false, 280 EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); 281 return getBatteryInfo( 282 context, 283 batteryBroadcast, 284 batteryUsageStats, 285 estimate, 286 elapsedRealtimeUs, 287 shortString); 288 } 289 290 @WorkerThread getBatteryInfo( Context context, Intent batteryBroadcast, @NonNull BatteryUsageStats batteryUsageStats, Estimate estimate, long elapsedRealtimeUs, boolean shortString, long currentTimeMs)291 public static BatteryInfo getBatteryInfo( 292 Context context, 293 Intent batteryBroadcast, 294 @NonNull BatteryUsageStats batteryUsageStats, 295 Estimate estimate, 296 long elapsedRealtimeUs, 297 boolean shortString, 298 long currentTimeMs) { 299 final boolean isCompactStatus = 300 context.getResources() 301 .getBoolean(com.android.settings.R.bool.config_use_compact_battery_status); 302 BatteryInfo info = new BatteryInfo(); 303 info.mBatteryUsageStats = batteryUsageStats; 304 info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast); 305 info.batteryPercentString = Utils.formatPercentage(info.batteryLevel); 306 info.pluggedStatus = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); 307 info.mCharging = info.pluggedStatus != 0; 308 info.averageTimeToDischarge = estimate.getAverageDischargeTime(); 309 info.isBatteryDefender = 310 batteryBroadcast.getIntExtra( 311 BatteryManager.EXTRA_CHARGING_STATUS, 312 BatteryManager.CHARGING_POLICY_DEFAULT) 313 == BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE; 314 315 info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus); 316 info.batteryStatus = 317 batteryBroadcast.getIntExtra( 318 BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); 319 info.isFastCharging = 320 BatteryStatus.getChargingSpeed(context, batteryBroadcast) 321 == BatteryStatus.CHARGING_FAST; 322 if (info.isBatteryDefender) { 323 info.isBatteryDefender = 324 FeatureFactory.getFeatureFactory() 325 .getPowerUsageFeatureProvider() 326 .isBatteryDefend(info); 327 } 328 if (!info.mCharging) { 329 updateBatteryInfoDischarging(context, shortString, estimate, info); 330 } else { 331 updateBatteryInfoCharging( 332 context, 333 batteryBroadcast, 334 batteryUsageStats, 335 info, 336 isCompactStatus, 337 currentTimeMs); 338 } 339 BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", currentTimeMs); 340 return info; 341 } 342 343 /** Returns a {@code BatteryInfo} with battery and charging relative information. */ 344 @WorkerThread getBatteryInfo( Context context, Intent batteryBroadcast, BatteryUsageStats batteryUsageStats, Estimate estimate, long elapsedRealtimeUs, boolean shortString)345 public static BatteryInfo getBatteryInfo( 346 Context context, 347 Intent batteryBroadcast, 348 BatteryUsageStats batteryUsageStats, 349 Estimate estimate, 350 long elapsedRealtimeUs, 351 boolean shortString) { 352 long currentTimeMs = System.currentTimeMillis(); 353 return getBatteryInfo( 354 context, 355 batteryBroadcast, 356 batteryUsageStats, 357 estimate, 358 elapsedRealtimeUs, 359 shortString, 360 currentTimeMs); 361 } 362 updateBatteryInfoCharging( Context context, Intent batteryBroadcast, BatteryUsageStats stats, BatteryInfo info, boolean compactStatus, long currentTimeMs)363 private static void updateBatteryInfoCharging( 364 Context context, 365 Intent batteryBroadcast, 366 BatteryUsageStats stats, 367 BatteryInfo info, 368 boolean compactStatus, 369 long currentTimeMs) { 370 final Resources resources = context.getResources(); 371 final long chargeTimeMs = stats.getChargeTimeRemainingMs(); 372 if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) { 373 Settings.Global.putLong( 374 context.getContentResolver(), 375 com.android.settingslib.fuelgauge.BatteryUtils.GLOBAL_TIME_TO_FULL_MILLIS, 376 chargeTimeMs); 377 } 378 379 final int status = 380 batteryBroadcast.getIntExtra( 381 BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); 382 info.discharging = false; 383 info.suggestionLabel = null; 384 int dockDefenderMode = BatteryUtils.getCurrentDockDefenderMode(context, info); 385 if ((info.isBatteryDefender 386 && status != BatteryManager.BATTERY_STATUS_FULL 387 && dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) 388 || dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) { 389 // Battery defender active, battery charging paused 390 info.remainingLabel = null; 391 int chargingLimitedResId = R.string.power_charging_limited; 392 info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString); 393 return; 394 } 395 final BatterySettingsFeatureProvider featureProvider = 396 FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider(); 397 if (featureProvider.isChargingOptimizationMode(context)) { 398 final CharSequence chargeLabel = 399 featureProvider.getChargingOptimizationChargeLabel( 400 context, 401 info.batteryLevel, 402 info.batteryPercentString, 403 chargeTimeMs, 404 currentTimeMs); 405 if (chargeLabel != null) { 406 final CharSequence remainingLabel = 407 featureProvider.getChargingOptimizationRemainingLabel( 408 context, 409 info.batteryLevel, 410 info.pluggedStatus, 411 chargeTimeMs, 412 currentTimeMs); 413 if (remainingLabel != null) { 414 info.chargeLabel = chargeLabel; 415 info.remainingLabel = remainingLabel; 416 return; 417 } 418 } 419 } 420 if ((chargeTimeMs > 0 421 && status != BatteryManager.BATTERY_STATUS_FULL 422 && dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) 423 || dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) { 424 // Battery is charging to full 425 info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs); 426 int resId = getChargingDurationResId(info.isFastCharging); 427 info.remainingLabel = 428 chargeTimeMs <= 0 429 ? null 430 : getPowerRemainingChargingLabel( 431 context, 432 chargeTimeMs, 433 info.isFastCharging, 434 info.pluggedStatus, 435 currentTimeMs, 436 featureProvider); 437 438 info.chargeLabel = 439 chargeTimeMs <= 0 440 ? info.batteryPercentString 441 : getChargeLabelWithTimeToFull( 442 context, 443 resId, 444 info.batteryPercentString, 445 chargeTimeMs, 446 info.isFastCharging, 447 currentTimeMs); 448 } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) { 449 // Dock defender will be triggered in the future, charging will be optimized. 450 info.chargeLabel = 451 context.getString( 452 R.string.power_charging_future_paused, info.batteryPercentString); 453 } else { 454 final String chargeStatusLabel = 455 Utils.getBatteryStatus(context, batteryBroadcast, compactStatus); 456 info.remainingLabel = null; 457 info.chargeLabel = 458 info.batteryLevel == 100 459 ? info.batteryPercentString 460 : resources.getString( 461 R.string.power_charging, 462 info.batteryPercentString, 463 chargeStatusLabel); 464 } 465 } 466 getPowerRemainingChargingLabel( Context context, long chargeRemainingTimeMs, boolean isFastCharging, int pluggedStatus, long currentTimeMs, BatterySettingsFeatureProvider featureProvider)467 private static CharSequence getPowerRemainingChargingLabel( 468 Context context, 469 long chargeRemainingTimeMs, 470 boolean isFastCharging, 471 int pluggedStatus, 472 long currentTimeMs, 473 BatterySettingsFeatureProvider featureProvider) { 474 if (pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) { 475 final CharSequence wirelessChargingRemainingLabel = 476 featureProvider.getWirelessChargingRemainingLabel( 477 context, chargeRemainingTimeMs, currentTimeMs); 478 if (wirelessChargingRemainingLabel != null) { 479 return wirelessChargingRemainingLabel; 480 } 481 } 482 if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) { 483 int chargeLabelResId = 484 isFastCharging 485 ? R.string.power_remaining_fast_charging_duration_only_v2 486 : R.string.power_remaining_charging_duration_only_v2; 487 String timeString = 488 PowerUtil.getTargetTimeShortString( 489 context, chargeRemainingTimeMs, currentTimeMs); 490 return context.getString(chargeLabelResId, timeString); 491 } 492 final CharSequence timeString = 493 StringUtil.formatElapsedTime( 494 context, 495 chargeRemainingTimeMs, 496 /* withSeconds= */ false, 497 /* collapseTimeUnit= */ true); 498 return context.getString(R.string.power_remaining_charging_duration_only, timeString); 499 } 500 getChargeLabelWithTimeToFull( Context context, int chargeLabelResId, String batteryPercentString, long chargeTimeMs, boolean isFastCharging, long currentTimeMs)501 private static CharSequence getChargeLabelWithTimeToFull( 502 Context context, 503 int chargeLabelResId, 504 String batteryPercentString, 505 long chargeTimeMs, 506 boolean isFastCharging, 507 long currentTimeMs) { 508 if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) { 509 var timeString = 510 PowerUtil.getTargetTimeShortString(context, chargeTimeMs, currentTimeMs); 511 512 return isFastCharging 513 ? context.getString( 514 chargeLabelResId, 515 batteryPercentString, 516 context.getString(R.string.battery_info_status_charging_fast_v2), 517 timeString) 518 : context.getString(chargeLabelResId, batteryPercentString, timeString); 519 } else { 520 var timeString = 521 StringUtil.formatElapsedTime( 522 context, 523 (double) chargeTimeMs, 524 /* withSeconds= */ false, 525 /* collapseTimeUnit= */ true); 526 return context.getString(chargeLabelResId, batteryPercentString, timeString); 527 } 528 } 529 getChargingDurationResId(boolean isFastCharging)530 private static int getChargingDurationResId(boolean isFastCharging) { 531 if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) { 532 return isFastCharging 533 ? R.string.power_fast_charging_duration_v2 534 : R.string.power_charging_duration_v2; 535 } 536 return R.string.power_charging_duration; 537 } 538 updateBatteryInfoDischarging( Context context, boolean shortString, Estimate estimate, BatteryInfo info)539 private static void updateBatteryInfoDischarging( 540 Context context, boolean shortString, Estimate estimate, BatteryInfo info) { 541 final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis()); 542 if (drainTimeUs > 0) { 543 info.remainingTimeUs = drainTimeUs; 544 info.remainingLabel = 545 PowerUtil.getBatteryRemainingShortStringFormatted( 546 context, PowerUtil.convertUsToMs(drainTimeUs)); 547 info.chargeLabel = info.remainingLabel; 548 info.suggestionLabel = 549 PowerUtil.getBatteryTipStringFormatted( 550 context, PowerUtil.convertUsToMs(drainTimeUs)); 551 } else { 552 info.remainingLabel = null; 553 info.suggestionLabel = null; 554 info.chargeLabel = info.batteryPercentString; 555 } 556 } 557 558 public interface BatteryDataParser { onParsingStarted(long startTime, long endTime)559 void onParsingStarted(long startTime, long endTime); 560 onDataPoint(long time, HistoryItem record)561 void onDataPoint(long time, HistoryItem record); 562 onDataGap()563 void onDataGap(); 564 onParsingDone()565 void onParsingDone(); 566 } 567 568 /** 569 * Iterates over battery history included in the BatteryUsageStats that this object was 570 * initialized with. 571 */ parseBatteryHistory(BatteryDataParser... parsers)572 public void parseBatteryHistory(BatteryDataParser... parsers) { 573 long startWalltime = 0; 574 long endWalltime = 0; 575 long historyStart = 0; 576 long historyEnd = 0; 577 long curWalltime = startWalltime; 578 long lastWallTime = 0; 579 long lastRealtime = 0; 580 int lastInteresting = 0; 581 int pos = 0; 582 boolean first = true; 583 final BatteryStatsHistoryIterator iterator1 = 584 mBatteryUsageStats.iterateBatteryStatsHistory(); 585 HistoryItem rec; 586 while ((rec = iterator1.next()) != null) { 587 pos++; 588 if (first) { 589 first = false; 590 historyStart = rec.time; 591 } 592 if (rec.cmd == HistoryItem.CMD_CURRENT_TIME || rec.cmd == HistoryItem.CMD_RESET) { 593 // If there is a ridiculously large jump in time, then we won't be 594 // able to create a good chart with that data, so just ignore the 595 // times we got before and pretend like our data extends back from 596 // the time we have now. 597 // Also, if we are getting a time change and we are less than 5 minutes 598 // since the start of the history real time, then also use this new 599 // time to compute the base time, since whatever time we had before is 600 // pretty much just noise. 601 if (rec.currentTime > (lastWallTime + (180 * 24 * 60 * 60 * 1000L)) 602 || rec.time < (historyStart + (5 * 60 * 1000L))) { 603 startWalltime = 0; 604 } 605 lastWallTime = rec.currentTime; 606 lastRealtime = rec.time; 607 if (startWalltime == 0) { 608 startWalltime = lastWallTime - (lastRealtime - historyStart); 609 } 610 } 611 if (rec.isDeltaData()) { 612 lastInteresting = pos; 613 historyEnd = rec.time; 614 } 615 } 616 617 endWalltime = lastWallTime + historyEnd - lastRealtime; 618 619 int i = 0; 620 final int N = lastInteresting; 621 622 for (int j = 0; j < parsers.length; j++) { 623 parsers[j].onParsingStarted(startWalltime, endWalltime); 624 } 625 626 if (endWalltime > startWalltime) { 627 final BatteryStatsHistoryIterator iterator2 = 628 mBatteryUsageStats.iterateBatteryStatsHistory(); 629 while ((rec = iterator2.next()) != null && i < N) { 630 if (rec.isDeltaData()) { 631 curWalltime += rec.time - lastRealtime; 632 lastRealtime = rec.time; 633 long x = (curWalltime - startWalltime); 634 if (x < 0) { 635 x = 0; 636 } 637 for (int j = 0; j < parsers.length; j++) { 638 parsers[j].onDataPoint(x, rec); 639 } 640 } else { 641 long lastWalltime = curWalltime; 642 if (rec.cmd == HistoryItem.CMD_CURRENT_TIME 643 || rec.cmd == HistoryItem.CMD_RESET) { 644 if (rec.currentTime >= startWalltime) { 645 curWalltime = rec.currentTime; 646 } else { 647 curWalltime = startWalltime + (rec.time - historyStart); 648 } 649 lastRealtime = rec.time; 650 } 651 652 if (rec.cmd != HistoryItem.CMD_OVERFLOW 653 && (rec.cmd != HistoryItem.CMD_CURRENT_TIME 654 || Math.abs(lastWalltime - curWalltime) > (60 * 60 * 1000))) { 655 for (int j = 0; j < parsers.length; j++) { 656 parsers[j].onDataGap(); 657 } 658 } 659 } 660 i++; 661 } 662 } 663 664 for (int j = 0; j < parsers.length; j++) { 665 parsers[j].onParsingDone(); 666 } 667 } 668 } 669