Android 电量的显示在statusBar里,属于SystemUI,源码在 :
frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.java
构造方法如下:
在batteryMeterView的构造方法中,new 了 一个ImageView,该VIew就是电池图标的View
通过setImageDrawable设置图片为mDrawable, mDrawable是通过代码绘制而成的
public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
defStyle, 0);
final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
context.getColor(R.color.meter_background_color));
mPercentageStyleId = atts.getResourceId(R.styleable.BatteryMeterView_textAppearance, 0);
mDrawable = new ThemedBatteryDrawable(context, frameColor);
atts.recycle();
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
mShowPercentAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_battery_percentage_setting_available);
addOnAttachStateChangeListener(
new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS,
Dependency.get(CommandQueue.class)));
setupLayoutTransition();
mSlotBattery = context.getString(
com.android.internal.R.string.status_bar_battery);
mBatteryIconView = new ImageView(context);
mBatteryIconView.setImageDrawable(mDrawable);
final MarginLayoutParams mlp = new MarginLayoutParams(
getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),
getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));
mlp.setMargins(0, 0, 0,
getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));
addView(mBatteryIconView, mlp);
updateShowPercent();
mDualToneHandler = new DualToneHandler(context);
// Init to not dark at all.
onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
@Override
public void onUserSwitched(int newUserId) {
mUser = newUserId;
getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
newUserId);
updateShowPercent();
}
};
setClipChildren(false);
setClipToPadding(false);
Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
}
当电量发生改变的时候 调用onBatteryLevelChanged
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mDrawable.setCharging(pluggedIn);
mDrawable.setBatteryLevel(level);
mCharging = pluggedIn;
mLevel = level;
updatePercentText();
}
private void updatePercentText() {
if (mBatteryController == null) {
return;
}
if (mBatteryPercentView != null) {
if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
if (estimate != null) {
mBatteryPercentView.setText(estimate);
setContentDescription(getContext().getString(
R.string.accessibility_battery_level_with_estimate,
mLevel, estimate));
} else {
setPercentTextAtCurrentLevel();
}
});
} else {
setPercentTextAtCurrentLevel();
}
} else {
setContentDescription(
getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
: R.string.accessibility_battery_level, mLevel));
}
}
private void setPercentTextAtCurrentLevel() {
mBatteryPercentView.setText(
NumberFormat.getPercentInstance().format(mLevel / 100f));
setContentDescription(
getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
: R.string.accessibility_battery_level, mLevel));
}
有线充电和无线充电的判断
mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
@VisibleForTesting
String computePowerIndication() {
if (mPowerCharged) {
return mContext.getResources().getString(R.string.keyguard_charged);
}
final boolean hasChargingTime = mChargingTimeRemaining > 0;
int chargingId;
if (mPowerPluggedInWired) {
switch (mChargingSpeed) {
case BatteryStatus.CHARGING_FAST:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_fast
: R.string.keyguard_plugged_in_charging_fast;
break;
case 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;
}
} else {
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_wireless
: R.string.keyguard_plugged_in_wireless;
}
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
if (hasChargingTime) {
// We now have battery percentage in these strings and it's expected that all
// locales will also have it in the future. For now, we still have to support the old
// format until all languages get the new translations.
String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, mChargingTimeRemaining);
try {
return mContext.getResources().getString(chargingId, chargingTimeFormatted,
percentage);
} catch (IllegalFormatConversionException e) {
return mContext.getResources().getString(chargingId, chargingTimeFormatted);
}
} else {
// Same as above
try {
return mContext.getResources().getString(chargingId, percentage);
} catch (IllegalFormatConversionException e) {
return mContext.getResources().getString(chargingId);
}
}
}
frameworks/base/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
public BatteryStatus(Intent batteryChangedIntent) {
status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
-1);
int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
if (maxChargingMicroVolt <= 0) {
maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
}
if (maxChargingMicroAmp > 0) {
// Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
// to maintain precision equally on both factors.
maxChargingWattage = (maxChargingMicroAmp / 1000)
* (maxChargingMicroVolt / 1000);
} else {
maxChargingWattage = -1;
}
}
判断是否是有线充电
public boolean isPluggedInWired() {
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB;
}
判断是否在充电
/**
* Determine whether the device is plugged in (USB, power, or wireless).
*
* @return true if the device is plugged in.
*/
public boolean isPluggedIn() {
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB
|| plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
充电速度的判断
/**
* Return current chargin speed is fast, slow or normal.
*
* @return the charing speed
*/
public final int getChargingSpeed(Context context) {
final int slowThreshold = context.getResources().getInteger(
R.integer.config_chargingSlowlyThreshold);
final int fastThreshold = context.getResources().getInteger(
R.integer.config_chargingFastThreshold);
return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
maxChargingWattage > fastThreshold ? CHARGING_FAST :
CHARGING_REGULAR;
}
frameworks\base\packages\SettingsLib\res\values\config.xml
<!-- These resources are around just to allow their values to be customized -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
<integer name="config_chargingSlowlyThreshold">5000000</integer>
<!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V -->
<integer name="config_chargingFastThreshold">7500000</integer>
</resources>
@Override
public void onRefreshBatteryInfo(BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
boolean wasPluggedIn = mPowerPluggedIn;
mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
mPowerCharged = status.isCharged();
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mContext);
mBatteryLevel = status.level;
try {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;
} catch (RemoteException e) {
Log.e(TAG, "Error calling IBatteryStats: ", e);
mChargingTimeRemaining = -1;
}
updateIndication(!wasPluggedIn && mPowerPluggedInWired);
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
showTransientIndication(computePowerIndication());
hideTransientIndicationDelayed(HIDE_DELAY_MS);
} else if (wasPluggedIn && !mPowerPluggedIn) {
hideTransientIndication();
}
}
}
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\policy\BatteryControllerImpl.java
该控制器监控系统广播的电池电量变化事件。
注册广播接收器
private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(ACTION_LEVEL_TEST);
mBroadcastDispatcher.registerReceiver(this, filter);
}
@Override
public void init() {
registerReceiver();
if (!mHasReceivedBattery) {
// Get initial state. Relying on Sticky behavior until API for getting info.
Intent intent = mContext.registerReceiver(
null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (intent != null && !mHasReceivedBattery) {
onReceive(mContext, intent);
}
}
updatePowerSave();
updateEstimate();
}
在回调的时候更新电量
@Override
public void addCallback(BatteryController.BatteryStateChangeCallback cb) {
synchronized (mChangeCallbacks) {
mChangeCallbacks.add(cb);
}
if (!mHasReceivedBattery) return;
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
cb.onPowerSaveChanged(mPowerSave);
}
@Override
public void removeCallback(BatteryController.BatteryStateChangeCallback cb) {
synchronized (mChangeCallbacks) {
mChangeCallbacks.remove(cb);
}
}
@Override
public void onReceive(final Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
mHasReceivedBattery = true;
mLevel = (int)(100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
} else if (action.equals(ACTION_LEVEL_TEST)) {
mTestmode = true;
mMainHandler.post(new Runnable() {
int curLevel = 0;
int incr = 1;
int saveLevel = mLevel;
boolean savePlugged = mPluggedIn;
Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
@Override
public void run() {
if (curLevel < 0) {
mTestmode = false;
dummy.putExtra("level", saveLevel);
dummy.putExtra("plugged", savePlugged);
dummy.putExtra("testmode", false);
} else {
dummy.putExtra("level", curLevel);
dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
: 0);
dummy.putExtra("testmode", true);
}
context.sendBroadcast(dummy);
if (!mTestmode) return;
curLevel += incr;
if (curLevel == 100) {
incr *= -1;
}
mMainHandler.postDelayed(this, 200);
}
});
}
}
调用回调更新电量
protected void fireBatteryLevelChanged() {
synchronized (mChangeCallbacks) {
final int N = mChangeCallbacks.size();
for (int i = 0; i < N; i++) {
mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
}
}
}
BatteryMeterView在onAttachedToWindow方法中addCallback
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mBatteryController = Dependency.get(BatteryController.class);
mBatteryController.addCallback(this);
mUser = ActivityManager.getCurrentUser();
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
getContext().getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
false, mSettingObserver);
updateShowPercent();
subscribeForTunerUpdates();
mUserTracker.startTracking();
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mDrawable.setCharging(pluggedIn);
mDrawable.setBatteryLevel(level);
mCharging = pluggedIn;
mLevel = level;
updatePercentText();
}
省电模式:
frameworks/base/core/java/android/os/PowerManager.java
/**
* Returns true if the device is currently in power save mode. When in this mode,
* applications should reduce their functionality in order to conserve battery as
* much as possible. You can monitor for changes to this state with
* {@link #ACTION_POWER_SAVE_MODE_CHANGED}.
*
* @return Returns true if currently in low power mode, else false.
*/
public boolean isPowerSaveMode() {
return mPowerSaveModeCache.query(null);
}
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
@Override // Binder call
public boolean isPowerSaveMode() {
final long ident = Binder.clearCallingIdentity();
try {
return mBatterySaverController.isEnabled();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy
三种省电模式
static final int POLICY_LEVEL_OFF = 0;
static final int POLICY_LEVEL_ADAPTIVE = 1;
static final int POLICY_LEVEL_FULL = 2;
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
@Override // Binder call
public boolean setPowerSaveModeEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
!= PackageManager.PERMISSION_GRANTED) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
}
final long ident = Binder.clearCallingIdentity();
try {
return setLowPowerModeInternal(enabled);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
充电状态下,不允许打开/关闭省电模式
private boolean setLowPowerModeInternal(boolean enabled) {
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
}
if (mIsPowered) {
return false;
}
mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);
return true;
}
}
打开省电模式时,通过 BatterySaverStateMachine.setBatterySaverEnabledManually() 方法,把指令传给状态机
frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
/**
* {@link com.android.server.power.PowerManagerService} calls it when
* {@link android.os.PowerManager#setPowerSaveModeEnabled} is called.
*
* Note this could? be called before {@link #onBootCompleted} too.
*/
public void setBatterySaverEnabledManually(boolean enabled) {
if (DEBUG) {
Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);
}
synchronized (mLock) {
updateStateLocked(true, enabled);
// TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true and
// enabled is false
}
}
状态机通过 updateStateLocked() 更新内部状态,然后根据状态执行相应的操作。 注意,这里的第一个参数表示是否是用户手动打开省电模式,值为 true,第二个参数表示是否打开省电模式
@GuardedBy("mLock")
private void updateStateLocked(boolean manual, boolean enable) {
if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
return; // Not fully initialized yet.
}
switch (mState) {
case STATE_OFF: {
if (!mIsPowered) {
if (manual) {
if (!enable) {
Slog.e(TAG, "Tried to disable BS when it's already OFF");
return;
}
enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
BatterySaverController.REASON_MANUAL_ON);
hideStickyDisabledNotification();
mState = STATE_MANUAL_ON;
} else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
enableBatterySaverLocked(/*enable*/ true, /*manual*/ false,
BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON);
hideStickyDisabledNotification();
mState = STATE_AUTOMATIC_ON;
} else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) {
enableBatterySaverLocked(/*enable*/ true, /*manual*/ false,
BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON);
hideStickyDisabledNotification();
mState = STATE_AUTOMATIC_ON;
}
}
break;
}
case STATE_MANUAL_ON: {
if (manual) {
if (enable) {
Slog.e(TAG, "Tried to enable BS when it's already MANUAL_ON");
return;
}
enableBatterySaverLocked(/*enable*/ false, /*manual*/ true,
BatterySaverController.REASON_MANUAL_OFF);
mState = STATE_OFF;
} else if (mIsPowered) {
enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
BatterySaverController.REASON_PLUGGED_IN);
if (mSettingBatterySaverEnabledSticky
&& !mBatterySaverStickyBehaviourDisabled) {
mState = STATE_PENDING_STICKY_ON;
} else {
mState = STATE_OFF;
}
}
break;
}
case STATE_AUTOMATIC_ON: {
if (mIsPowered) {
enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
BatterySaverController.REASON_PLUGGED_IN);
mState = STATE_OFF;
} else if (manual) {
if (enable) {
Slog.e(TAG, "Tried to enable BS when it's already AUTO_ON");
return;
}
enableBatterySaverLocked(/*enable*/ false, /*manual*/ true,
BatterySaverController.REASON_MANUAL_OFF);
// When battery saver is disabled manually (while battery saver is enabled)
// when the battery level is low, we "snooze" BS -- i.e. disable auto battery
// saver.
// We resume auto-BS once the battery level is not low, or the device is
// plugged in.
mState = STATE_OFF_AUTOMATIC_SNOOZED;
} else if (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) {
enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF);
mState = STATE_OFF;
} else if (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) {
enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF);
mState = STATE_OFF;
} else if (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked()) {
enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
BatterySaverController.REASON_SETTING_CHANGED);
mState = STATE_OFF;
}
break;
}
case STATE_OFF_AUTOMATIC_SNOOZED: {
if (manual) {
if (!enable) {
Slog.e(TAG, "Tried to disable BS when it's already AUTO_SNOOZED");
return;
}
enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
BatterySaverController.REASON_MANUAL_ON);
mState = STATE_MANUAL_ON;
} else if (mIsPowered // Plugging in resets snooze.
|| (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked())
|| (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked())
|| (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked())) {
mState = STATE_OFF;
}
break;
}
case STATE_PENDING_STICKY_ON: {
if (manual) {
// This shouldn't be possible. We'll only be in this state when the device is
// plugged in, so the user shouldn't be able to manually change state.
Slog.e(TAG, "Tried to manually change BS state from PENDING_STICKY_ON");
return;
}
final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled
&& mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold;
final boolean isStickyDisabled =
mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
if (isStickyDisabled || shouldTurnOffSticky) {
mState = STATE_OFF;
setStickyActive(false);
triggerStickyDisabledNotification();
} else if (!mIsPowered) {
// Re-enable BS.
enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
BatterySaverController.REASON_STICKY_RESTORE);
mState = STATE_MANUAL_ON;
}
break;
}
default:
Slog.wtf(TAG, "Unknown state: " + mState);
break;
}
}
状态机里的默认状态是 STATE_OFF,表示省电模式默认关闭。
通过 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON);
打开省电模式,然后把状态切换为 STATE_MANUAL_ON。对于每一次状态切换,我们都要注意,因此这会影响下一次状态切换。