公司最近在搞个KMS,鼓励大家技术分享。好吧,我是一个好员工,积极响应领导的各种要求。但是,这个我不会写的,你可以说我没有分享精神,可以说我技术水平low,我就是不会写。如果公司一定要强制要求我写,我也一定是乱写凑数,这完全不能怪我,只能怪这个无情的世界,无情的季节,无情的………….。(请不要问我具体的原因,谢谢!)
但是,有一个好朋友,在上面发表了一个电池低电处理的文章,我看了一下,确实是说的非常清楚,只是是一个比较低版本的代码流程,我在android 7.1的代码上看了一下,和他说的有一点出入,不过,这没有关系不影响我们来学习。
我可以不写,但是我们可以学习。
BatteryService.java
./frameworks/base/services/core/java/com/android/server/BatteryService.java
当电池电量少于我们配置的低电量警告值(config_lowBatteryWarningLevel)时,发送低电量通知,弹出低电量通知对话框。
./frameworks/base/core/res/res/values/config.xml
<integer name="config_lowBatteryWarningLevel">15</integer>
只我满足shouldSendBatteryLowLocked的条件就可以发送低电量通知:
private boolean shouldSendBatteryLowLocked() {
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
/* The ACTION_BATTERY_LOW broadcast is sent in these situations:
* - is just un-plugged (previously was plugged) and battery level is
* less than or equal to WARNING, or
* - is not plugged and battery level falls to WARNING boundary
* (becomes <= mLowBatteryWarningLevel).
*/
return !plugged
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
发送低电量通知Intent.ACTION_BATTERY_LOW:
private void processValuesLocked(boolean force)
if (shouldSendBatteryLowLocked()) {
mSentLowBatteryBroadcast = true;
mHandler.post(new Runnable() {
@Override
public void run() {
Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
当电量为0时,发出关闭通知Intent.ACTION_REQUEST_SHUTDOWN:
private void shutdownIfWeakChargerEmptySOCLocked() {
if (mBatteryProps.batteryLevel == 0) {
if (mInitiateShutdown) {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
PowerNotificationWarnings.java
./frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
当电量低时,弹出低电量通知对话框:
private void showWarningNotification() {
final int textRes = R.string.battery_low_percent_format;
final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
final Notification.Builder nb = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.ic_power_low)
// Bump the notification when the bucket dropped.
.setWhen(mBucketDroppedNegativeTimeMs)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.battery_low_title))
.setContentText(mContext.getString(textRes, percentage))
.setOnlyAlertOnce(true)
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
.setPriority(Notification.PRIORITY_MAX)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setColor(mContext.getColor(
com.android.internal.R.color.battery_saver_mode_color));
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
}
nb.addAction(0,
mContext.getString(R.string.battery_saver_start_action),
pendingBroadcast(ACTION_START_SAVER));
if (mPlaySound) {
attachLowBatterySound(nb);
mPlaySound = false;
}
SystemUI.overrideNotificationAppName(mContext, nb);
mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
}
并播放低电量声音:
private void attachLowBatterySound(Notification.Builder b) {
final ContentResolver cr = mContext.getContentResolver();
final int silenceAfter = Settings.Global.getInt(cr,
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);
final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;
if (silenceAfter > 0
&& mScreenOffTime > 0
&& offTime > silenceAfter) {
Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter
+ "ms): not waking up the user with low battery sound");
return;
}
if (DEBUG) {
Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated
}
if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
final String soundPath = Settings.Global.getString(cr,
Settings.Global.LOW_BATTERY_SOUND);
if (soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
b.setSound(soundUri, AUDIO_ATTRIBUTES);
if (DEBUG) Slog.d(TAG, "playing sound " + soundUri);
}
}
}
}
我们一般在配置文件中设置低电量的声音文件和超时时间:
./frameworks/base/packages/SettingsProvider/res/values/defaults.xml
<string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
<!-- Default for Settings.Global.LOW_BATTERY_SOUND_TIMEOUT.
<integer name="def_low_battery_sound_timeout">0</integer>
而事实上调用showWarningNotification方法,弹出低电量通知对话框的地方为updateNotification():
private void updateNotification() {
if (mInvalidCharger) {
showInvalidChargerNotification();
mShowing = SHOWING_INVALID_CHARGER;
} else if (mWarning) {
showWarningNotification();
再往上查找调用updateNotification() 为:
public void showLowBatteryWarning(boolean playSound) {
mPlaySound = playSound;
mWarning = true;
updateNotification();
}
再往上查找调用showLowBatteryWarning() 为:
./frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
private final class Receiver extends BroadcastReceiver {
public void init() {
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
mContext.registerReceiver(this, filter, null, mHandler);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
final int oldBatteryStatus = mBatteryStatus;
mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
final int oldPlugType = mPlugType;
mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
final int oldInvalidCharger = mInvalidCharger;
mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
final boolean plugged = mPlugType != 0;
final boolean oldPlugged = oldPlugType != 0;
int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);
mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
mWarnings.showInvalidChargerWarning();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
mWarnings.dismissInvalidChargerWarning();
} else if (mWarnings.isInvalidChargerWarningShowing()) {
return;
}
boolean isPowerSaver = mPowerManager.isPowerSaveMode();
if (!plugged
&& !isPowerSaver
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < 0) {
// only play SFX when the dialog comes up or the bucket changes
final boolean playSound = bucket != oldBucket || oldPlugged;
mWarnings.showLowBatteryWarning(playSound);
可见,现在弹出低电量通知对话框是接受广播Intent.ACTION_BATTERY_CHANGED来实现的。