版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/su749520/article/details/85202294
1. 现象
为什么第一次开机或者格式化后电池显示上次充满电是xxx天或者xxx分钟前,不管之前电池电量数值
实际操作:
- 充满电且拔除充电线,则显示上次充满电为0分钟前
- 新机器第一次开机,不论当前电量大小,拔除充电线,则显示上次充满电为0分钟前
- 修改系统时间未来时间,则显示上次充满电是xxx天或者xxx分钟前
- 修改系统时间过去时间,则显示为0分钟前
2. 原因
因为上次充满电接口getStartClockTime中 mStartClockTime = currentTime - (mClocks.elapsedRealtime()/当前自开机后,经过的时间,包括深度休眠的时间/-(mRealtimeStart/1000))决定,其中elapsedRealtime和mRealtimeStart的数值刷新逻辑BatteryStatsImpl->setBatteryStateLocked->setOnBatteryLocked->resetAllStatsLocked->initTimes改变
故这里显示上次充满电是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();