对电池的变化都会监听BatteryService发出的Intent.ACTION_BATTERY_CHANGED广播
KeyguardUpdateMonitor.java
onRecive中
else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
final int maxChargingCurrent = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
final Message msg = mHandler.obtainMessage(
MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
maxChargingCurrent));
mHandler.sendMessage(msg);
MSG_BATTERY_UPDATE 广播
private void handleBatteryUpdate(BatteryStatus status) {
if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);
mBatteryStatus = status;
if (batteryUpdateInteresting) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onRefreshBatteryInfo(status);
}
}
}
}
KeyguardIndicationController.java
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
mPowerCharged = status.isCharged();
mChargingCurrent = status.maxChargingCurrent;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
updateIndication();
}
updateIndication
private void updateIndication() {
if (mVisible) {
mTextView.switchIndication(computeIndication());
mTextView.setTextColor(computeColor());
}
}
computeIndication
private String computeIndication() {
if (!TextUtils.isEmpty(mTransientIndication)) {
return mTransientIndication;
}
if (mPowerPluggedIn) {
String indication = computePowerIndication();
if (DEBUG_CHARGING_CURRENT) {
indication += ", " + (mChargingCurrent / 1000) + " mA";
}
return indication;
}
return mRestingIndication;
}
private String computePowerIndication() {
if (mPowerCharged) {
return mContext.getResources().getString(R.string.keyguard_charged);
}
// Try fetching charging time from battery stats.
long chargingTimeRemaining = 0;
try {
chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();//剩余时间通过BatteryStatsService获取
} catch (RemoteException e) {
Log.e(TAG, "Error calling IBatteryStats: ", e);
}
final boolean hasChargingTime = chargingTimeRemaining > 0;
int chargingId;
switch (mChargingSpeed) {
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_fast
: R.string.keyguard_plugged_in_charging_fast;
break;
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_slowly
: R.string.keyguard_plugged_in_charging_slowly;
break;
default:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time
: R.string.keyguard_plugged_in;
break;
}
if (hasChargingTime) {
String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, chargingTimeRemaining);
return mContext.getResources().getString(chargingId, chargingTimeFormatted);
} else {
return mContext.getResources().getString(chargingId);
}
}
mBatteryInfo赋值
mBatteryInfo = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
BatteryStatsService.java
public long computeChargeTimeRemaining() {
synchronized (mStats) {
long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());
return time >= 0 ? (time/1000) : time;
}
}
mStats是在BatteryService构造函数中赋值的
BatteryStatsService(File systemDir, Handler handler) {
// Our handler here will be accessing the disk, use a different thread than
// what the ActivityManagerService gave us (no I/O on that one!).
mHandler = new BatteryStatsHandler(FgThread.getHandler().getLooper());
// BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
}
BatteryStatsImpl.java
public long computeBatteryTimeRemaining(long curTime) {
if (!mOnBattery) {
return -1;
}
/* Simple implementation just looks at the average discharge per level across the
entire sample period.
int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
if (discharge < 2) {
return -1;
}
long duration = computeBatteryRealtime(curTime, STATS_SINCE_CHARGED);
if (duration < 1000*1000) {
return -1;
}
long usPerLevel = duration/discharge;
return usPerLevel * mCurrentBatteryLevel;
*/
if (mDischargeStepTracker.mNumStepDurations < 1) {
return -1;
}
long msPerLevel = mDischargeStepTracker.computeTimePerLevel();
if (msPerLevel <= 0) {
return -1;
}
return (msPerLevel * mCurrentBatteryLevel) * 1000;//因此msperlevel代表每一个level需要多少时间,100-mCurrentBatteryLevel代表需要充电的电量
public long computeTimePerLevel() {
final long[] steps = mStepDurations;
final int numSteps = mNumStepDurations;
// For now we'll do a simple average across all steps.
if (numSteps <= 0) {
return -1;
}
long total = 0;
for (int i=0; i<numSteps; i++) {
total += steps[i] & STEP_LEVEL_TIME_MASK;//高位保存的别的信息,需要将它去除
}
return total / numSteps;//所有的level的时间和除以总的level,就是每个level的平均时间
}
在BatteryService更新电池信息时候,如下:会调用BatteryStatsService的setBatteryState函数
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked(false);
} else {
mLastBatteryProps.set(props);
}
}
}
private void processValuesLocked(boolean force){
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,//setBatteryState
mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
mBatteryProps.batteryVoltage);
} catch (RemoteException e) {
// Should never happen.
}
}
BatteryStatsService.java
setBatteryState
public void setBatteryState(final int status, final int health, final int plugType,
final int level, final int temp, final int volt) {
enforceCallingPermission();
// BatteryService calls us here and we may update external state. It would be wrong
// to block such a low level service like BatteryService on external stats like WiFi.
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mStats) {
final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
if (mStats.isOnBattery() == onBattery) {
// The battery state has not changed, so we don't need to sync external
// stats immediately.
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
return;
}
}
// Sync external stats first as the battery has changed states. If we don't sync
// immediately here, we may not collect the relevant data later.
updateExternalStats("battery-state", UPDATE_ALL);
synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
}
}
});
}
BatteryStatsImpl.java
setBatteryStateLocked
public static final int BATTERY_PLUGGED_NONE = 0;
public void setBatteryStateLocked(int status, int health, int plugType, int level,
int temp, int volt) {
final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
final long uptime = SystemClock.uptimeMillis();
final long elapsedRealtime = SystemClock.elapsedRealtime();
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
// We start out assuming that the device is plugged in (not
// on battery). If our first report is now that we are indeed
// plugged in, then twiddle our state to correctly reflect that
// since we won't be going through the full setOnBattery().
if (onBattery == mOnBattery) {
if (onBattery) {
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
} else {
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
}
}
// Always start out assuming charging, that will be updated later.
mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryLevel = (byte)level;
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
mLastChargingStateLevel = level;
} else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
}
int oldStatus = mHistoryCur.batteryStatus;
if (onBattery) {
mDischargeCurrentLevel = level;
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
}
} else if (level < 96) {
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
}
}
mCurrentBatteryLevel = level;
if (mDischargePlugLevel < 0) {
mDischargePlugLevel = level;
}
if (onBattery != mOnBattery) {
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryHealth = (byte)health;
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char)volt;
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
mHistoryCur.batteryLevel = (byte)level;
changed = true;
// TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
// which will pull external stats.
scheduleSyncExternalStatsLocked("battery-level");
}
if (mHistoryCur.batteryStatus != status) {
mHistoryCur.batteryStatus = (byte)status;
changed = true;
}
if (mHistoryCur.batteryHealth != health) {
mHistoryCur.batteryHealth = (byte)health;
changed = true;
}
if (mHistoryCur.batteryPlugType != plugType) {
mHistoryCur.batteryPlugType = (byte)plugType;
changed = true;
}
if (temp >= (mHistoryCur.batteryTemperature+10)
|| temp <= (mHistoryCur.batteryTemperature-10)) {
mHistoryCur.batteryTemperature = (short)temp;
changed = true;
}
if (volt > (mHistoryCur.batteryVoltage+20)
|| volt < (mHistoryCur.batteryVoltage-20)) {
mHistoryCur.batteryVoltage = (char)volt;
changed = true;
}
long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
| (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
| (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
if (onBattery) {
changed |= setChargingLocked(false);
if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
modeBits, elapsedRealtime);
mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
modeBits, elapsedRealtime);
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
} else {
if (level >= 90) {
// If the battery level is at least 90%, always consider the device to be
// charging even if it happens to go down a level.
changed |= setChargingLocked(true);
mLastChargeStepLevel = level;
} if (!mCharging) {
if (mLastChargeStepLevel < level) {
// We have not reporting that we are charging, but the level has now
// gone up, so consider the state to be charging.
changed |= setChargingLocked(true);
mLastChargeStepLevel = level;
}
} else {
if (mLastChargeStepLevel > level) {
// We had reported that the device was charging, but here we are with
// power connected and the level going down. Looks like the current
// power supplied isn't enough, so consider the device to now be
// discharging.
changed |= setChargingLocked(false);
mLastChargeStepLevel = level;
}
}
if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
modeBits, elapsedRealtime);
mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
modeBits, elapsedRealtime);
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
}
if (changed) {
addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
// We don't record history while we are plugged in and fully charged.
// The next time we are unplugged, history will be cleared.
mRecordingHistory = DEBUG;
}
}
BatteryStats.java
addLevelSteps函数
public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
int stepCount = mNumStepDurations;
final long lastStepTime = mLastStepTime;
if (lastStepTime >= 0 && numStepLevels > 0) {
final long[] steps = mStepDurations;
long duration = elapsedRealtime - lastStepTime;//这次充电用了多少时间
for (int i=0; i<numStepLevels; i++) {
System.arraycopy(steps, 0, steps, 1, steps.length-1);/这个函数的意思是将数组steps从位置0,长度steps.length-1,copy到位置1
long thisDuration = duration / (numStepLevels-i);
duration -= thisDuration;
if (thisDuration > STEP_LEVEL_TIME_MASK) {
thisDuration = STEP_LEVEL_TIME_MASK;
}
steps[0] = thisDuration | modeBits;
}
stepCount += numStepLevels;
if (stepCount > steps.length) {
stepCount = steps.length;
}
}
mNumStepDurations = stepCount;
mLastStepTime = elapsedRealtime;
}
因此计算每格电池的平均充电时间,每格电池所花的时间之和除以就是所有的level即 mNumStepDurations。就是平均每格电池的充电时间。
public long computeTimePerLevel() {
final long[] steps = mStepDurations;
final int numSteps = mNumStepDurations;
// For now we'll do a simple average across all steps.
if (numSteps <= 0) {
return -1;
}
long total = 0;
for (int i=0; i<numSteps; i++) {
total += steps[i] & STEP_LEVEL_TIME_MASK;//高位保存的别的信息,需要将它去除
}
return total / numSteps;//所有的level的时间和除以总的level,就是每个level的平均时间
}
batterystats.bin
public void readLocked() {
if (mDailyFile != null) {
readDailyStatsLocked();
}
if (mFile == null) {
Slog.w("BatteryStats", "readLocked: no file associated with this instance");
return;
}
mUidStats.clear();
try {
File file = mFile.chooseForRead();
if (!file.exists()) {
return;
}
FileInputStream stream = new FileInputStream(file);
byte[] raw = BatteryStatsHelper.readFully(stream);
Parcel in = Parcel.obtain();
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
stream.close();
readSummaryFromParcel(in);
} catch(Exception e) {
Slog.e("BatteryStats", "Error reading battery statistics", e);
resetAllStatsLocked();
}
mEndPlatformVersion = Build.ID;
if (mHistoryBuffer.dataPosition() > 0) {
mRecordingHistory = true;
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
if (USE_OLD_HISTORY) {
addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
}
addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtime, uptime, false);
}
recordDailyStatsIfNeededLocked(false);
}
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
if (version != VERSION) {
Slog.w("BatteryStats", "readFromParcel: version got " + version
+ ", expected " + VERSION + "; erasing old stats");
return;
}
readHistory(in, true);
mStartCount = in.readInt();
mUptime = in.readLong();
mRealtime = in.readLong();
mStartClockTime = in.readLong();
mStartPlatformVersion = in.readString();
mEndPlatformVersion = in.readString();
mOnBatteryTimeBase.readSummaryFromParcel(in);
mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
mCurrentBatteryLevel = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
int NPKG = in.readInt();
.......
}
写入batteryStats.bin接口
public void writeAsyncLocked() {
writeLocked(false);
}
public void writeSyncLocked() {
writeLocked(true);
}
void writeLocked(boolean sync) {
if (mFile == null) {
Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
return;
}
if (mShuttingDown) {
return;
}
Parcel out = Parcel.obtain();
writeSummaryToParcel(out, true);
mLastWriteTime = SystemClock.elapsedRealtime();
if (mPendingWrite != null) {
mPendingWrite.recycle();
}
mPendingWrite = out;
if (sync) {
commitPendingDataToDisk();
} else {
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
commitPendingDataToDisk();
}
});
}
}
在ActivityManagerService中新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取"batterystats.bin"的值,来初始化自己的成员变量。
比如在关机的时候进行写,但这时候是同步的。
新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取"batterystats.bin"的值,来初始化自己的成员变量。
比如说充电,一开机,之前的充电的每格电池的充电时间都没有,通过从"batterystats.bin"文件中读取,就可以将上次开机的状态知道了。
插入usb充电时,由于mOnBattery 改变了会调用setOnBatteryLocked函数,插上usb线,在锁屏状态下。需要充两格电或者以上,才会有剩余时间的显示(待验证)