最近遇到个新的需求,当电池温度过高以及OVP状态的时候,在用户界面提示信息。
电池电源信息相关在Framework层/
与电池电量信息等相关类:
1.BatteryManager.java
2.BatteryProperties.java
3.BatteryStates.java
4.PowerUI.java
5.LightService.java
6.PowerManager.java
7.BatteryService.java
这里先分析BatteryService.java。
源码路径:
***/framework/base/services/java/com/android/server/BatteryService.java
BatteryService.java代码,必要的地方做了处理。
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import android.os.BatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.BatteryProperties;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesListener;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.DropBoxManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
* battery. When these values change this service broadcasts the new values
* to all {@link android.content.BroadcastReceiver IntentReceivers} that are
* watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
* BATTERY_CHANGED} action.</p>
* <p>The new values are stored in the Intent data and can be retrieved by
* calling {@link android.content.Intent#getExtra Intent.getExtra} with the
* following keys:</p>
* <p>"scale" - int, the maximum value for the charge level</p>
* <p>"level" - int, charge level, from 0 through "scale" inclusive</p>
* <p>"status" - String, the current charging status.<br />
* <p>"health" - String, the current battery health.<br />
* <p>"present" - boolean, true if the battery is present<br />
* <p>"icon-small" - int, suggested small icon to use for this state</p>
* <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged
* into an AC power adapter; 2 if plugged in via USB.</p>
* <p>"voltage" - int, current battery voltage in millivolts</p>
* <p>"temperature" - int, current battery temperature in tenths of
* a degree Centigrade</p>
* <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p>
*
* <p>
* The battery service may be called by the power manager while holding its locks so
* we take care to post all outcalls into the activity manager to a handler.
*
* FIXME: Ideally the power manager would perform all of its calls into the battery
* service asynchronously itself.
* </p>
*/
public final class BatteryService extends Binder {
private static final String TAG = BatteryService.class.getSimpleName();
private static final boolean DEBUG = false;
//电量百分比显示
private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
private static final int DUMP_MAX_LENGTH = 24 * 1024;
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
private static final String DUMPSYS_DATA_PATH = "/data/system/";
// This should probably be exposed in the API, though it's not critical
//没有充电状态则显示0
private static final int BATTERY_PLUGGED_NONE = 0;
private final Context mContext;
private final IBatteryStats mBatteryStats;
private final Handler mHandler;
private final Object mLock = new Object();
private BatteryProperties mBatteryProps;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
private boolean mLastBatteryPresent;
private int mLastBatteryLevel;
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
private int mInvalidCharger;
private int mLastInvalidCharger;
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
private long mDischargeStartTime;
private int mDischargeStartLevel;
private boolean mUpdatesStopped;
private Led mLed;
private boolean mSentLowBatteryBroadcast = false;
private BatteryListener mBatteryPropertiesListener;
private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
public BatteryService(Context context, LightsService lights) {
mContext = context;
mHandler = new Handler(true /*async*/);
mLed = new Led(context, lights);
mBatteryStats = BatteryStatsService.getService();
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
//res/res/values/config.xml:547: <integer name="config_lowBatteryWarningLevel">15</integer>
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
//res/res/values/config.xml:550: <integer name="config_lowBatteryCloseWarningLevel">20</integer> 接近警戒值。
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
//res/res/values/config.xml:544: <integer name="config_shutdownBatteryTemperature">680</integer>//68度?为什么?
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
// watch for invalid charger messages if the invalid_charger switch exists
if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
mInvalidChargerObserver.startObserving(
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
//注册监听。实时更新电池电量信息,底层传递上来的电池信息有变化时,则上层会做响应的处理。
//从这里可以看出回调接口中调用的update()方法应该是比较频繁的。
mBatteryPropertiesListener = new BatteryListener();
IBinder b = ServiceManager.getService("batterypropreg");
mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);
} catch (RemoteException e) {
// Should never happen.
}
}
//该方法在SystemServer.java中调用。开机过程。
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
}
}
/**
* Returns true if the device is plugged into any of the specified plug types.
*/
public boolean isPowered(int plugTypeSet) {
synchronized (mLock) {
return isPoweredLocked(plugTypeSet);
}
}
//当前手机是否正在充电。这里面查查&的相关用法。
private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
return true;
}
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
return true;
}
if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
return true;
}
return false;
}
/**
* Returns the current plug type.
*/
public int getPlugType() {
synchronized (mLock) {
return mPlugType;
}
}
/**
* Returns battery level as a percentage.
*/
public int getBatteryLevel() {
synchronized (mLock) {
return mBatteryProps.batteryLevel;
}
}
/**
* Returns true if battery level is below the first warning threshold.
*/
public boolean isBatteryLow() {
synchronized (mLock) {
return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;
}
}
/**
* Returns a non-zero value if an unsupported charger is attached.
*/
public int getInvalidCharger() {
synchronized (mLock) {
return mInvalidCharger;
}
}
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
mHandler.post(new Runnable() {
@Override
public void run() {
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);
}
}
});
}
}
private void shutdownIfOverTempLocked() {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
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);
}
}
});
}
}
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
//mBatteryProps即BatteryProperties,该类封装了电量信息的一些参数。实现了Parcelable接口。
mBatteryProps = props;
// Process the new values.
processValuesLocked();
}
}
}
//这里是处理电量等逻辑的主要位置。
private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;
mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
//当前的充电方式。AC,USB,WIRELESS(第三种有点高大上)
if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
//打印日志,可以忽略。
if (DEBUG) {
Slog.d(TAG, "Processing new values: "
+ "chargerAcOnline=" + mBatteryProps.chargerAcOnline
+ ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ ", batteryStatus=" + mBatteryProps.batteryStatus
+ ", batteryHealth=" + mBatteryProps.batteryHealth
+ ", batteryPresent=" + mBatteryProps.batteryPresent
+ ", batteryLevel=" + mBatteryProps.batteryLevel
+ ", batteryTechnology=" + mBatteryProps.batteryTechnology
+ ", batteryVoltage=" + mBatteryProps.batteryVoltage
+ ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow
+ ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
+ ", batteryTemperature=" + mBatteryProps.batteryTemperature
+ ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ ", mPlugType=" + mPlugType);
}
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
mBatteryProps.batteryVoltage);
} catch (RemoteException e) {
// Should never happen.
}
//电池电量耗尽以及温度过高。
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
//mBatteryProps保存之前的电量信息,这里做对比当数据信息有变化的时候则表示电池状态有变化。
if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mBatteryProps.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
mInvalidCharger != mLastInvalidCharger) {
if (mPlugType != mLastPlugType) {
//plugType前后变化,充电状态--->非充电状态,非充电状态---->充电状态
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
// discharging -> charging
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
mDischargeStartLevel, mBatteryProps.batteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
//打印Log,忽略++++++++++++++++++begin
if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
mPlugType, mBatteryProps.batteryTechnology);
}
if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
}
///打印Log,忽略++++++++++++++++++end
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;//当前的充电状态
final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;//上次的充电状态
//处理低电量状态。发送广播,弹出AlertDialog,提示用户电池电量低。
/* 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).
*/
final boolean sendBatteryLow = !plugged//当前没有充电
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于警戒值
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次在充或者上次的电量大于警戒值。
//更新电池电量显示
sendIntentLocked();
//++++++++++++++++power connected / not connected begin+++++++++++++++++++++++++++++++++++++++++++++
//充电电源连接与充电电源拔出。
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
if (mPlugType != 0 && mLastPlugType == 0) {
mHandler.post(new Runnable() {
@Override
public void run() {
//android4.4/framework/base/services/java/com/android/server/EntropyMixer.java:100
Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
else if (mPlugType == 0 && mLastPlugType != 0) {
mHandler.post(new Runnable() {
@Override
public void run() {
//么有找到该广播处理。
Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
//++++++++++++++power connected / not connected end+++++++++++++++++++++++++++++++++++++++++++++++
//当前是否处于低电量状态
//这两个中ACTION_BATTERY_LOW和ACTION_BATTERY_OKAY没有找到对应的处理,因此先忽略。这里的ACTION_BATTERY_LOW
//与低电量的显示没有多大关系.
if (sendBatteryLow) {
//当前处于低电量状态,发送该广播
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);
}
});
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
//当处于低电量时发送ACTION_BATTERY_LOW广播后,用户插上了电源充电,此时,电池电量增加变化,如果变化到大于了20%,则会发送该广播电池状态正常OK状态。
mSentLowBatteryBroadcast = false;
mHandler.post(new Runnable() {
@Override
public void run() {
Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
// Update the battery LED
mLed.updateLightsLocked();
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlierLocked(dischargeDuration);
}
//当前的电量信息更新完毕,保存为最新状态与下一次更新比较。
mLastBatteryStatus = mBatteryProps.batteryStatus;
mLastBatteryHealth = mBatteryProps.batteryHealth;
mLastBatteryPresent = mBatteryProps.batteryPresent;
mLastBatteryLevel = mBatteryProps.batteryLevel;
mLastPlugType = mPlugType;
mLastBatteryVoltage = mBatteryProps.batteryVoltage;
mLastBatteryTemperature = mBatteryProps.batteryTemperature;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
}
//发送广播ACTION:ACTION_BATTERY_CHANGED,发送当前电量信息,更新UI显示,该广播的处理在SystemUI中的PowerUI.java中。
private void sendIntentLocked() {
// Pack up the values and broadcast them to everyone
final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
int icon = getIconLocked(mBatteryProps.batteryLevel);
//将最新的电池电量信息封装到intent中,主动发送给SystemUI,让其显示。
intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);//这个表示啥意思?
intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
//日志打印,可以忽略。
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
", health:" + mBatteryProps.batteryHealth + ", present:" + mBatteryProps.batteryPresent +
", voltage: " + mBatteryProps.batteryVoltage +
", temperature: " + mBatteryProps.batteryTemperature +
", technology: " + mBatteryProps.batteryTechnology +
", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +
", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
", icon:" + icon + ", invalid charger:" + mInvalidCharger);
}
mHandler.post(new Runnable() {
@Override
public void run() {
ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
}
});
}
private void logBatteryStatsLocked() {
IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);
if (batteryInfoService == null) return;
DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;
File dumpFile = null;
FileOutputStream dumpStream = null;
try {
// dump the service to a file
dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");
dumpStream = new FileOutputStream(dumpFile);
batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
FileUtils.sync(dumpStream);
// add dump file to drop box
db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);
} catch (RemoteException e) {
Slog.e(TAG, "failed to dump battery service", e);
} catch (IOException e) {
Slog.e(TAG, "failed to write dumpsys file", e);
} finally {
// make sure we clean up
if (dumpStream != null) {
try {
dumpStream.close();
} catch (IOException e) {
Slog.e(TAG, "failed to close dumpsys output stream");
}
}
if (dumpFile != null && !dumpFile.delete()) {
Slog.e(TAG, "failed to delete temporary dumpsys file: "
+ dumpFile.getAbsolutePath());
}
}
}
private void logOutlierLocked(long duration) {
ContentResolver cr = mContext.getContentResolver();
String dischargeThresholdString = Settings.Global.getString(cr,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD);
String durationThresholdString = Settings.Global.getString(cr,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);
if (dischargeThresholdString != null && durationThresholdString != null) {
try {
long durationThreshold = Long.parseLong(durationThresholdString);
int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
if (duration <= durationThreshold &&
mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {
// If the discharge cycle is bad enough we want to know about it.
logBatteryStatsLocked();
}
if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
" discharge threshold: " + dischargeThreshold);
if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
(mDischargeStartLevel - mBatteryProps.batteryLevel));
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
durationThresholdString + " or " + dischargeThresholdString);
return;
}
}
}
//获取当前电池图标显示的drawableID
//BATTERY_STATUS_DISCHARGING和BATTERY_STATUS_NOT_CHARGING应该差不多,因为从逻辑里面看两者状态下都是用了相同的drawable资源。
private int getIconLocked(int level) {
if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {//充电
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
return com.android.internal.R.drawable.stat_sys_battery;
} else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
|| mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
&& mBatteryProps.batteryLevel >= 100) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else {
return com.android.internal.R.drawable.stat_sys_battery;
}
} else {
return com.android.internal.R.drawable.stat_sys_battery_unknown;
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump Battery service from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
pw.println(" status: " + mBatteryProps.batteryStatus);
pw.println(" health: " + mBatteryProps.batteryHealth);
pw.println(" present: " + mBatteryProps.batteryPresent);
pw.println(" level: " + mBatteryProps.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mBatteryProps.batteryVoltage);
if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {
pw.println(" current now: " + mBatteryProps.batteryCurrentNow);
}
if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {
pw.println(" charge counter: " + mBatteryProps.batteryChargeCounter);
}
pw.println(" temperature: " + mBatteryProps.batteryTemperature);
pw.println(" technology: " + mBatteryProps.batteryTechnology);
} else if (args.length == 3 && "set".equals(args[0])) {
String key = args[1];
String value = args[2];
try {
boolean update = true;
if ("ac".equals(key)) {
mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
} else if ("usb".equals(key)) {
mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
} else if ("wireless".equals(key)) {
mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
} else if ("status".equals(key)) {
mBatteryProps.batteryStatus = Integer.parseInt(value);
} else if ("level".equals(key)) {
mBatteryProps.batteryLevel = Integer.parseInt(value);
} else if ("invalid".equals(key)) {
mInvalidCharger = Integer.parseInt(value);
} else {
pw.println("Unknown set option: " + key);
update = false;
}
if (update) {
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
processValuesLocked();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
}
} else if (args.length == 1 && "reset".equals(args[0])) {
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = false;
} finally {
Binder.restoreCallingIdentity(ident);
}
} else {
pw.println("Dump current battery state, or:");
pw.println(" set ac|usb|wireless|status|level|invalid <value>");
pw.println(" reset");
}
}
}
private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
synchronized (mLock) {
if (mInvalidCharger != invalidCharger) {
mInvalidCharger = invalidCharger;
}
}
}
};
private final class Led {
private final LightsService.Light mBatteryLight;
private final int mBatteryLowARGB;
private final int mBatteryMediumARGB;
private final int mBatteryFullARGB;
private final int mBatteryLedOn;
private final int mBatteryLedOff;
public Led(Context context, LightsService lights) {
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
//./core/res/res/values/config.xml:562: <integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer>
mBatteryLowARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLowARGB);
//./core/res/res/values/config.xml:565: <integer name="config_notificationsBatteryMediumARGB">0xFFFFFF00</integer>
mBatteryMediumARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
//./core/res/res/values/config.xml:568: <integer name="config_notificationsBatteryFullARGB">0xFF00FF00</integer>
mBatteryFullARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryFullARGB);
//./core/res/res/values/config.xml:571: <integer name="config_notificationsBatteryLedOn">125</integer>
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
//./core/res/res/values/config.xml:577: <integer name="config_notificationsBatteryLedOff">2875</integer>
mBatteryLedOff = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOff);
}
/**
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
final int level = mBatteryProps.batteryLevel;
final int status = mBatteryProps.batteryStatus;
if (level < mLowBatteryWarningLevel) { //当前电量处于低电量状态
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {//处于低电量状态,并且正在充电
// Solid red when battery is charging
mBatteryLight.setColor(mBatteryLowARGB);
} else {//低电量状态但是没有充电
// Flash red when battery is low and not charging
mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING//当前属于充电状态
|| status == BatteryManager.BATTERY_STATUS_FULL) {
if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
// Solid green when full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
// Solid orange when charging and halfway full
mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
// No lights if not charging and not low
mBatteryLight.turnOff();
}
}
}
private final class BatteryListener extends IBatteryPropertiesListener.Stub {
public void batteryPropertiesChanged(BatteryProperties props) {
BatteryService.this.update(props);
}
}
}
上面看完了,BatteryService.java的源码,感觉不多, 因此这里就平时使用手机的体验来看看相关的一些具体实现吧。反正源码在手,随便你看。
1.手机电量过低,弹出来的dialog信息框是从哪里实现的?
sendIntentLocked() 方法中ACTION为ACTION_BATTERY_CHANGED的广播,在SystemUI里面接收参数判断处理。
这个直接在PowerUI.java中查看。从接收广播,到解析数据,然后针对数据的各个情况做处理。
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@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);
//充电类型。AC,USB,WIRELESS
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;
//从方法来看,貌似就是电池的状态是Ok的呢还是电量过低,
int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);
if (DEBUG) {
Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
}
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Slog.d(TAG, "showing invalid charger warning");
showInvalidChargerDialog();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
dismissInvalidChargerDialog();
} else if (mInvalidChargerDialog != null) {
// if invalid charger is showing, don't show low battery
return;
}
if (!plugged
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < 0) {
showLowBatteryWarning();
// only play SFX when the dialog comes up or the bucket changes
if (bucket != oldBucket || oldPlugged) {//可以体会到,当我们手机没电了,恰好我们把充电拔了,就会有提示音。
playLowBatterySound();
}
} else if (plugged || (bucket > oldBucket && bucket > 0)) {
//充电状态
dismissLowBatteryWarning();
} else if (mBatteryLevelTextView != null) {
//显示低电量dialog
showLowBatteryWarning();
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOffTime = -1;
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
}
};
代码比较清晰,主要是电池信息的一些情况判断,需要多多琢磨一下为啥是这样的。
//显示低电量Dialog
void showLowBatteryWarning() {
Slog.i(TAG,
((mBatteryLevelTextView == null) ? "showing" : "updating")
+ " low battery warning: level=" + mBatteryLevel
+ " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
CharSequence levelText = mContext.getString(
R.string.battery_low_percent_format, mBatteryLevel);
//这里的第一个if:因为电池的刷新频率比较高,因此如果此时dialog还在显示状态中,直接刷新电量信息即可。
if (mBatteryLevelTextView != null) {
mBatteryLevelTextView.setText(levelText);
} else {
View v = View.inflate(mContext, R.layout.battery_low, null);
mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
mBatteryLevelTextView.setText(levelText);
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
b.setCancelable(true);
b.setTitle(R.string.battery_low_title);
b.setView(v);
b.setIconAttribute(android.R.attr.alertDialogIcon);
b.setPositiveButton(android.R.string.ok, null);
final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_NO_HISTORY);
if (intent.resolveActivity(mContext.getPackageManager()) != null) {
b.setNegativeButton(R.string.battery_low_why,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
dismissLowBatteryWarning();
}
});
}
AlertDialog d = b.create();
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mLowBatteryDialog = null;
mBatteryLevelTextView = null;
}
});
//dialog属性
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
mLowBatteryDialog = d;
}
}
我要做的需求差不多就可以按照低电量的显示来实现了。这里省略。
2.拔掉充电器或者USB连线,状态栏的电池图标是如何更新的?
当插拔数据线的时候,电池图标的变化,是自定义View Draw出来的。具体在
packages/SystemUI/src/com/android/systemui/BatteryMeterView.java 。
3.低电量关机是如何实现的?
首先我们可以从BatteryService.java中看到有这么个方法:
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
mHandler.post(new Runnable() {
@Override
public void run() {
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);
}
}
});
}
}
--->搜索可以找到AndroidMenifest.xml
<activity android:name="com.android.server.ShutdownActivity"
android:permission="android.permission.SHUTDOWN"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.REBOOT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
之前遇到一个问题,我们像从app上去实现关机,具体做法再研究了。
其中有人做了,但是涉及到一个权限就是
android:permission="android.permission.SHUTDOWN"
其中,android.excludeFromRecents="true" 表示当前打开的Activity所属应用,不会显示在最近使用的列表里。
/*
* from BatteryService.java
*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);
*
*/
Intent intent = getIntent();
mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());//from BatteryService传递过来的是Intent.ACTION_REQUEST_SHUTDOWN,这里肯定是false。
mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
Thread thr = new Thread("ShutdownActivity") {
@Override
public void run() {
IPowerManager pm = IPowerManager.Stub.asInterface(
ServiceManager.getService(Context.POWER_SERVICE));
try {
//根据Action需求,是重启还是关机。
if (mReboot) {
//接下来。。。。。
pm.reboot(mConfirm, null, false);
} else {
pm.shutdown(mConfirm, false);
}
} catch (RemoteException e) {
}
}
};
thr.start();
finish();
// Wait for us to tell the power manager to shutdown.
try {
thr.join();
} catch (InterruptedException e) {
}
}
8.app开发过程中,我们需要获取电池电量信息,如何来做?
当我们在开发app的时候需要获取电池信息,可以仿照BatteryService来做。 因为,BatteryService主动把电池电量信息传递给了SystemUI.
简单的做法就是:
getApplication().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//这里的intent封装了我们在BatterySerivce.java中sendIntentLocked()
//封装的电池电量信息。
}
}, filter) ;
4.pm.reboot,pm.shutdown 的后续流程是如何实现的?
5.LED灯显示 LightService
6.电池电量管理的底层研究。
7.BatteryService发送广播传递过程如何实现的。
比如这样的
Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
这里就先源码跟踪到这里,下次继续补充。