[電池]設定-電池-上次充滿電時間計算
阿新 • • 發佈:2019-01-12
1. 現象
為什麼第一次開機或者格式化後電池顯示上次充滿電是xxx天或者xxx分鐘前,不管之前電池電量數值
實際操作:
- 充滿電且拔除充電線,則顯示上次充滿電為0分鐘前
- 新機器第一次開機,不論當前電量大小,拔除充電線,則顯示上次充滿電為0分鐘前
- 修改系統時間未來時間,則顯示上次充滿電是xxx天或者xxx分鐘前
- 修改系統時間過去時間,則顯示為0分鐘前
2. 原因
因為上次充滿電介面getStartClockTime中 mStartClockTime = currentTime - (mClocks.elapsedRealtime()/當前自開機後,經過的時間,包括深度休眠的時間
故這裡顯示上次充滿電是xxx天或者xxx分鐘前,是因為沒有實際充電的時刻時,取的上次充滿電時間為系統當前時間.故會有明明沒充滿電,顯示上次充滿電是xxx天或者xxx分鐘前的邏輯顯示。除非充滿電且拔除充電線則為正常顯示了。
Google的這個邏輯確實存在不精確
3. 原始碼
3.1 設定->電池
package com.android.settings.fuelgauge; public class PowerUsageSummary extends PowerUsageBase implements OnLongClickListener, BatteryTipPreferenceController.BatteryTipListener { @VisibleForTesting void updateLastFullChargePreference() { if (mBatteryInfo != null && mBatteryInfo.averageTimeToDischarge != Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN) { // 電池在充滿電後的預估使用時間 } else { final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper, System.currentTimeMillis()); mLastFullChargePref.setTitle(R.string.battery_last_full_charge); // 上次充滿電 mLastFullChargePref.setSubtitle( StringUtil.formatRelativeTime(getContext(), lastFullChargeTime, false /* withSeconds */)); } }
3.2 BatteryUtils.calculateLastFullChargeTime
package com.android.settings.fuelgauge;
/**
* Utils for battery operation
*/
public class BatteryUtils {
/**
* Calculate the time since last full charge, including the device off time
* 計算上次充電電時間,包含裝置關機時間
*
* @param batteryStatsHelper utility class that contains the data 電池資訊類
* @param currentTimeMs current wall time 當前時間
* @return time in millis 返回時長
*/
public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper,
long currentTimeMs) {
return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime();
}
3.3 BatteryStatsHelper.getStats().getStartClockTime()
- frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
package com.android.internal.os;
public class BatteryStatsHelper {
private BatteryStats mStats;
public BatteryStats getStats() {
if (mStats == null) {
load();
}
return mStats;
}
private static BatteryStatsImpl getStats(IBatteryStats service) {
try {
ParcelFileDescriptor pfd = service.getStatisticsStream();
if (pfd != null) {
try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
Parcel parcel = Parcel.obtain();
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
.createFromParcel(parcel);
return stats;
} catch (IOException e) {
Log.w(TAG, "Unable to read statistics stream", e);
}
}
} catch (RemoteException e) {
Log.w(TAG, "RemoteException:", e);
}
return new BatteryStatsImpl();
}
3.3.1 BatteryStats.getStartClockTime()
- frameworks/base/core/java/android/os/BatteryStats.java
package android.os;
public abstract class BatteryStats implements Parcelable {
/**
* Return the wall clock time when battery stats data collection started.
*/
public abstract long getStartClockTime()
3.3.2 BatteryStatsImpl.getStartClockTime()
- frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
package com.android.internal.os;
public class BatteryStatsImpl extends BatteryStats {
// 獲取上次充滿電時間
@Override public long getStartClockTime() {
final long currentTime = System.currentTimeMillis();
if (ensureStartClockTime(currentTime)) {
recordCurrentTimeChangeLocked(currentTime, mClocks.elapsedRealtime(),
mClocks.uptimeMillis());
}
return mStartClockTime;
}
3.4 BatteryStatsImpl.getStartClockTime
3.4.1 BatteryStatsImpl.getStartClockTime.ensureStartClockTime
package com.android.internal.os;
public class BatteryStatsImpl extends BatteryStats {
long mStartClockTime;
void initTimes(long uptime, long realtime) {
...
mStartClockTime = System.currentTimeMillis();
...
}
// 從電池詳情資料庫中 batterystats.bin 更新StartClockTime
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
...
// 從 batterystats.bin 獲取上次充滿電時間
mStartClockTime = in.readLong();
mRealtimeStart = in.readLong();
...
}
public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
...
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
long startClockTime = getStartClockTime();
out.writeLong(startClockTime);
out.writeLong(mRealtimeStart);
...
}
// 是否需要重新校準上次充滿電時間
boolean ensureStartClockTime(final long currentTime) {
final long ABOUT_ONE_YEAR = 365*24*60*60*1000L; // 1 年時間 31536000000
// 是否滿足以下條件
// 1. 當前時間毫秒值 大於 1年,例如這裡是100%滿足,2018-12-22 10:41:46,即當前時間毫秒值 1545446651000
// 2. 上次充滿電時間StartClockTime 小於 當前時間毫秒值與1年的差, 即這裡即記錄最長上次充滿電1年內,即我手動調整時間,最長只能記錄上次充滿電365天內
// 或者滿足下面條件
// 1. 上次充滿電時間StartClockTime 大於 當前時間毫秒值,這個條件可以將系統時間調整為過去的時間
if ((currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR))
|| (mStartClockTime > currentTime)) {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
mStartClockTime = currentTime - (mClocks.elapsedRealtime()/*獲取從裝置boot後經歷的時間值*/-(mRealtimeStart/1000));
return true;
}
return false;
}
3.4.2 檢視 mStartClockTime 的賦值情況
- BatteryStatsImpl 初始化時間
private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
PlatformIdleStateCallback cb,
UserInfoProvider userInfoProvider) {
...
long uptime = mClocks.uptimeMillis() * 1000;
long realtime = mClocks.elapsedRealtime() * 1000;
initTimes(uptime, realtime)
- resetAllStatsLocked 初始化時間
private void resetAllStatsLocked() {
final long uptimeMillis = mClocks.uptimeMillis();
final long elapsedRealtimeMillis = mClocks.elapsedRealtime();
initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
- initTimes 函式
public interface Clocks {
public long elapsedRealtime();
public long uptimeMillis();
}
public static class SystemClocks implements Clocks {
public long elapsedRealtime() {
return SystemClock.elapsedRealtime(); // 自開機後,經過的時間,包括深度休眠的時間
}
public long uptimeMillis() {
return SystemClock.uptimeMillis(); // 自開機後,經過的時間,不包括深度休眠的時間
}
}
void initTimes(long uptime, long realtime) {
mStartClockTime = System.currentTimeMillis(); // 系統當前時間,即日期時間,可以被系統設定修改,如果設定系統時間,時間值會發生跳變。
mOnBatteryTimeBase.init(uptime, realtime);
mOnBatteryScreenOffTimeBase.init(uptime, realtime);
mRealtimeStart = realtime;
}
3.5 BatteryStatsImpl.setBatteryStateLocked.setOnBatteryLocked.resetAllStatsLocked
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
final int chargeFullUAh) {
if (onBattery != mOnBattery) {
...
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
@GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
// we have gone through a significant charge (from a very low
// level to a now very high level).
boolean reset = false;
if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
|| (mDischargeCurrentLevel < 20 && level >= 80)
|| (getHighDischargeAmountSinceCharge() >= 200
&& mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) {
...
resetAllStatsLocked();