最近工作接触到这么的东西,这是我对整个电池管理方面Java 层的分析。如果想了解底层的话,请看我的博客:
android 4.4 电池电量管理底层分析(C\C++层) (http://blog.csdn.NET/daweibalang717/article/details/41446993)
先贴一张类与类之间的关系图:
Android开机过程中会加载系统BatteryService ,说一下电池电量相关的,本文主要讲述关于JAVA 层代码。文件路径:\frameworks\base\services\java\com\android\server\BatteryService.java 下面贴出源码。我把注释加上。个人理解,仅参考。
[java] view plaincopy
/*
* 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;
/**
*
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.
*
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:
*
"scale" - int, the maximum value for the charge level
*
"level" - int, charge level, from 0 through "scale" inclusive
*
"status" - String, the current charging status.
*
"health" - String, the current battery health.
*
"present" - boolean, true if the battery is present
*
"icon-small" - int, suggested small icon to use for this state
*
"plugged" - int, 0 if the device is not plugged in; 1 if plugged
* into an AC power adapter; 2 if plugged in via USB.
*
"voltage" - int, current battery voltage in millivolts
*
"temperature" - int, current battery temperature in tenths of
* a degree Centigrade
*
"technology" - String, the type of battery installed, e.g. "Li-ion"
*
*
* 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.
*
*/
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
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();
//低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
//低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
//电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
//值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。
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(BatteryProperties props);
mBatteryPropertiesListener = new BatteryListener();
IBinder b = ServiceManager.getService("batterypropreg");
mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
//这里注册
mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);
} catch (RemoteException e) {
// Should never happen.
}
}
//开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
}
}
//返回是否在充电,这个函数在PowerManagerService.java 中调用
/**
* 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;
}
//USB,插电脑上充电
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.
*/
//电池属性:电量等级(0-100)
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 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra("cant_be_cancel_by_button", true);
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("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}
//这个方法就是被JNI回调的。用来更新上层状态的方法。
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked();
}
}
}
//嗯。这个就是最主要的方法了。
private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;
mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
//充电器类型
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();
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) {//当前充电器类型与上次的不一样
//并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。
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;
}
//并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
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;
}
//本次调用,当前的充电状态
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).
*/
//用于发送低电量广播的判断
final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15
sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED
// 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() {
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);
}
});
}
//发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)
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) {//电量超过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;
}
}
//电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)
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.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;
}
}
}
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
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);
mBatteryLowARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLowARGB);
mBatteryMediumARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
mBatteryFullARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryFullARGB);
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
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);
}
}
}
[java] view plain copy
/*
* 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;
/**
*
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.
*
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:
*
"scale" - int, the maximum value for the charge level
*
"level" - int, charge level, from 0 through "scale" inclusive
*
"status" - String, the current charging status.
*
"health" - String, the current battery health.
*
"present" - boolean, true if the battery is present
*
"icon-small" - int, suggested small icon to use for this state
*
"plugged" - int, 0 if the device is not plugged in; 1 if plugged
* into an AC power adapter; 2 if plugged in via USB.
*
"voltage" - int, current battery voltage in millivolts
*
"temperature" - int, current battery temperature in tenths of
* a degree Centigrade
*
"technology" - String, the type of battery installed, e.g. "Li-ion"
*
*
* 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.
*
*/
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
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();
//低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
//低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
//电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
//值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。
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(BatteryProperties props);
mBatteryPropertiesListener = new BatteryListener();
IBinder b = ServiceManager.getService("batterypropreg");
mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
//这里注册
mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);
} catch (RemoteException e) {
// Should never happen.
}
}
//开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
}
}
//返回是否在充电,这个函数在PowerManagerService.java 中调用
/**
* 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;
}
//USB,插电脑上充电
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.
*/
//电池属性:电量等级(0-100)
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 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra("cant_be_cancel_by_button", true);
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("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}
});
}
}
//这个方法就是被JNI回调的。用来更新上层状态的方法。
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked();
}
}
}
//嗯。这个就是最主要的方法了。
private void processValuesLocked() {
boolean logOutlier = false;
long dischargeDuration = 0;
mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
//充电器类型
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();
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) {//当前充电器类型与上次的不一样
//并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。
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;
}
//并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
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;
}
//本次调用,当前的充电状态
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).
*/
//用于发送低电量广播的判断
final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电
&& mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN
&& mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15
sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED
// 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() {
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);
}
});
}
//发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)
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) {//电量超过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;
}
}
//电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)
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.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;
}
}
}
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
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);
mBatteryLowARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLowARGB);
mBatteryMediumARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
mBatteryFullARGB = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryFullARGB);
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
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);
}
}
}
总结如下:此服务构造时会注册监听到系统JNI层。 当电池电量改变的时候会调用update(BatteryProperties props) -----》processValuesLocked() 。 而processValuesLocked() 函数会把电池状态把广播发送出去。其他类再接收广播进行处理
下一个类就是低电量提醒了,文件目录:\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.java
[java] view plaincopy
/*
* Copyright (C) 2008 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.systemui.power;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED
static final String TAG = "PowerUI";
static final boolean DEBUG = false;
Handler mHandler = new Handler();
int mBatteryLevel = 100;
int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
int mPlugType = 0;
int mInvalidCharger = 0;
int mLowBatteryAlertCloseLevel;
int[] mLowBatteryReminderLevels = new int[2];
AlertDialog mInvalidChargerDialog;
AlertDialog mLowBatteryDialog;
TextView mBatteryLevelTextView;
private long mScreenOffTime = -1;
public void start() { //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。
mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
// 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);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
}
/**
* Buckets the battery level.
*
* The code in this function is a little weird because I couldn't comprehend
* the bucket going up when the battery level was going down. --joeo
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
* less than 0 means that the battery is low
*/
private int findBatteryLevelBucket(int level) { //这个方法是用来警告判断用的。
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
if (level >= mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
for (int i=N-1; i>=0; i--) {
if (level <= mLowBatteryReminderLevels[i]) {
return -1-i;
}
}
throw new RuntimeException("not possible!");
}
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; //下边就是根据Intent获取BatteryService传过来的电池属性
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);
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)) { //插上充电器或者充电电池电量超过20取消弹窗
dismissLowBatteryWarning();
} else if (mBatteryLevelTextView != null) {
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);
}
}
};
void dismissLowBatteryWarning() {
if (mLowBatteryDialog != null) {
Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
mLowBatteryDialog.dismiss();
}
}
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 (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;
}
});
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
mLowBatteryDialog = d;
}
}
void playLowBatterySound() {
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) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
}
}
}
}
}
void dismissInvalidChargerDialog() {
if (mInvalidChargerDialog != null) {
mInvalidChargerDialog.dismiss();
}
}
void showInvalidChargerDialog() {
Slog.d(TAG, "showing invalid charger dialog");
dismissLowBatteryWarning();
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
b.setCancelable(true);
b.setMessage(R.string.invalid_charger);
b.setIconAttribute(android.R.attr.alertDialogIcon);
b.setPositiveButton(android.R.string.ok, null);
AlertDialog d = b.create();
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
mInvalidChargerDialog = null;
mBatteryLevelTextView = null;
}
});
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.show();
mInvalidChargerDialog = d;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
pw.print("mLowBatteryReminderLevels=");
pw.println(Arrays.toString(mLowBatteryReminderLevels));
pw.print("mInvalidChargerDialog=");
pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
pw.print("mLowBatteryDialog=");
pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
pw.print("mBatteryLevel=");
pw.println(Integer.toString(mBatteryLevel));
pw.print("mBatteryStatus=");
pw.println(Integer.toString(mBatteryStatus));
pw.print("mPlugType=");
pw.println(Integer.toString(mPlugType));
pw.print("mInvalidCharger=");
pw.println(Integer.toString(mInvalidCharger));
pw.print("mScreenOffTime=");
pw.print(mScreenOffTime);
if (mScreenOffTime >= 0) {
pw.print(" (");
pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
pw.print(" ago)");
}
pw.println();
pw.print("soundTimeout=");
pw.println(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
}
}
[java] view plain copy
/*
* Copyright (C) 2008 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.systemui.power;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED
static final String TAG = "PowerUI";
static final boolean DEBUG = false;
Handler mHandler = new Handler();
int mBatteryLevel = 100;
int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
int mPlugType = 0;
int mInvalidCharger = 0;
int mLowBatteryAlertCloseLevel;
int[] mLowBatteryReminderLevels = new int[2];
AlertDialog mInvalidChargerDialog;
AlertDialog mLowBatteryDialog;
TextView mBatteryLevelTextView;
private long mScreenOffTime = -1;
public void start() { //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。
mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
// 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);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
}
/**
* Buckets the battery level.
*
* The code in this function is a little weird because I couldn't comprehend
* the bucket going up when the battery level was going down. --joeo
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
* less than 0 means that the battery is low
*/
private int findBatteryLevelBucket(int level) { //这个方法是用来警告判断用的。
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
if (level >= mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
for (int i=N-1; i>=0; i--) {
if (level <= mLowBatteryReminderLevels[i]) {
return -1-i;
}
}
throw new RuntimeException("not possible!");
}
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; //下边就是根据Intent获取BatteryService传过来的电池属性
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);
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)) { //插上充电器或者充电电池电量超过20取消弹窗
dismissLowBatteryWarning();
} else if (mBatteryLevelTextView != null) {
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);
}
}
};
void dismissLowBatteryWarning() {
if (mLowBatteryDialog != null) {
Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
mLowBatteryDialog.dismiss();
}
}
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 (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;
}
});
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
mLowBatteryDialog = d;
}
}
void playLowBatterySound() {
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) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
}
}
}
}
}
void dismissInvalidChargerDialog() {
if (mInvalidChargerDialog != null) {
mInvalidChargerDialog.dismiss();
}
}
void showInvalidChargerDialog() {
Slog.d(TAG, "showing invalid charger dialog");
dismissLowBatteryWarning();
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
b.setCancelable(true);
b.setMessage(R.string.invalid_charger);
b.setIconAttribute(android.R.attr.alertDialogIcon);
b.setPositiveButton(android.R.string.ok, null);
AlertDialog d = b.create();
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
mInvalidChargerDialog = null;
mBatteryLevelTextView = null;
}
});
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.show();
mInvalidChargerDialog = d;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
pw.print("mLowBatteryReminderLevels=");
pw.println(Arrays.toString(mLowBatteryReminderLevels));
pw.print("mInvalidChargerDialog=");
pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
pw.print("mLowBatteryDialog=");
pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
pw.print("mBatteryLevel=");
pw.println(Integer.toString(mBatteryLevel));
pw.print("mBatteryStatus=");
pw.println(Integer.toString(mBatteryStatus));
pw.print("mPlugType=");
pw.println(Integer.toString(mPlugType));
pw.print("mInvalidCharger=");
pw.println(Integer.toString(mInvalidCharger));
pw.print("mScreenOffTime=");
pw.print(mScreenOffTime);
if (mScreenOffTime >= 0) {
pw.print(" (");
pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
pw.print(" ago)");
}
pw.println();
pw.print("soundTimeout=");
pw.println(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
}
}
总结一下:这个类就是接收电池电量改变的广播,然后弹窗提醒。
下一个类就是没电关机界面了,文件目录:\frameworks\base\services\java\com\android\server\ShutdownLowBatteryActivity.java
这个Activity的配置文件是这样的(\frameworks\base\core\res\AndroidManifest.xml):
[html] view plaincopy
android:configChanges="orientation|keyboardHidden|screenSize"
android:windowSoftInputMode="stateHidden"
android:permission="android.permission.SHUTDOWN"
android:excludeFromRecents="true">
[html] view plain copy
android:configChanges="orientation|keyboardHidden|screenSize"
android:windowSoftInputMode="stateHidden"
android:permission="android.permission.SHUTDOWN"
android:excludeFromRecents="true">
android:theme="@android:style/Theme.Translucent.NoTitleBar"是API小于11的透明主题。这样弹出的dialog会是老版本的主题。(这个Activity是透明的)
对于Theme,因为是4.4,我更倾向于:android:theme="@android:style/Theme.Holo.Panel"
ShutdownLowBatteryActivity.java:
[java] view plaincopy
/*
* Copyright (C) 2009 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.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;
//import com.android.internal.app.ShutdownThread;
import com.android.server.power.ShutdownThread;
import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.content.ContentResolver;
import android.provider.Settings;
import java.io.IOException;
public class ShutdownLowBatteryActivity extends Activity {
private static final String TAG = "ShutdownLowBatteryActivity";
private boolean mConfirm;
private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机
private AlertDialog mDialog;
private Handler myHandler = new Handler();
private Runnable myRunnable = new Runnable() { //这里数秒关机
@Override
public void run() {
mSeconds --;
if(mSeconds <1)
mSeconds=0;
mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));
if(mSeconds <= 1){
myHandler.removeCallbacks(myRunnable);
Handler h = new Handler();
h.post(new Runnable() {
public void run() {
ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);
}
});
}
myHandler.postDelayed(myRunnable,1000);
}
};
private BroadcastReceiver mReceiver;
private MediaPlayer mplayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
//if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|
Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){
ShutDownWakeLock.releaseCpuLock();
myHandler.removeCallbacks(myRunnable);
if(mReceiver != null)
unregisterReceiver(mReceiver);
finish();
}
}
};
IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_BATTERY_OKAY);
registerReceiver(mReceiver, filter);
//}
PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机
@Override
public void onCallStateChanged(int state, String ignored) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
ShutDownWakeLock.releaseCpuLock();
myHandler.removeCallbacks(myRunnable);
finish();
}
}
};
TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(new View(this));
mDialog=new AlertDialog.Builder(this).create();
mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);
mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));
if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮
mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
myHandler.removeCallbacks(myRunnable);
dialog.cancel();
if(mReceiver != null)
unregisterReceiver(mReceiver);
finish();
}});
}
mDialog.setCancelable(false);
//mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mDialog.show();
if(mConfirm == false){
myHandler.postDelayed(myRunnable, 1000);
}
myHandler.post(new Runnable(){
public void run(){
final ContentResolver cr = getContentResolver();
String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);
mplayer=new MediaPlayer();
try{
mplayer.reset();
mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");
mplayer.prepare();
mplayer.start();
mplayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if(null != mplayer){
mplayer.stop();
mplayer.release();
mplayer = null;
}
}
});
}
catch(IOException e){
}
}
});
}
}
[java] view plain copy
/*
* Copyright (C) 2009 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.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;
//import com.android.internal.app.ShutdownThread;
import com.android.server.power.ShutdownThread;
import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.content.ContentResolver;
import android.provider.Settings;
import java.io.IOException;
public class ShutdownLowBatteryActivity extends Activity {
private static final String TAG = "ShutdownLowBatteryActivity";
private boolean mConfirm;
private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机
private AlertDialog mDialog;
private Handler myHandler = new Handler();
private Runnable myRunnable = new Runnable() { //这里数秒关机
@Override
public void run() {
mSeconds --;
if(mSeconds <1)
mSeconds=0;
mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));
if(mSeconds <= 1){
myHandler.removeCallbacks(myRunnable);
Handler h = new Handler();
h.post(new Runnable() {
public void run() {
ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);
}
});
}
myHandler.postDelayed(myRunnable,1000);
}
};
private BroadcastReceiver mReceiver;
private MediaPlayer mplayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
//if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|
Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){
ShutDownWakeLock.releaseCpuLock();
myHandler.removeCallbacks(myRunnable);
if(mReceiver != null)
unregisterReceiver(mReceiver);
finish();
}
}
};
IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_BATTERY_OKAY);
registerReceiver(mReceiver, filter);
//}
PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机
@Override
public void onCallStateChanged(int state, String ignored) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
ShutDownWakeLock.releaseCpuLock();
myHandler.removeCallbacks(myRunnable);
finish();
}
}
};
TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(new View(this));
mDialog=new AlertDialog.Builder(this).create();
mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);
mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));
if(!getIntent().getBooleanExtra("cant_be_cancel_by_button", false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮
mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
myHandler.removeCallbacks(myRunnable);
dialog.cancel();
if(mReceiver != null)
unregisterReceiver(mReceiver);
finish();
}});
}
mDialog.setCancelable(false);
//mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mDialog.show();
if(mConfirm == false){
myHandler.postDelayed(myRunnable, 1000);
}
myHandler.post(new Runnable(){
public void run(){
final ContentResolver cr = getContentResolver();
String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);
mplayer=new MediaPlayer();
try{
mplayer.reset();
mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");
mplayer.prepare();
mplayer.start();
mplayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if(null != mplayer){
mplayer.stop();
mplayer.release();
mplayer = null;
}
}
});
}
catch(IOException e){
}
}
});
}
}
总结如下:这个类就是弹窗计时,提醒不连充电器就要关机了。
最后一个相关的类,文件路径:\frameworks\base\packages\SystemUI\src\com\android\systemui\BatteryMeterView.java
这个类是自定义控件,定义在手机状态栏里:
[java] view plaincopy
/*
* Copyright (C) 2013 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.systemui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
public class BatteryMeterView extends View implements DemoMode {
public static final String TAG = BatteryMeterView.class.getSimpleName();
public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
public static final boolean ENABLE_PERCENT = true;
public static final boolean SINGLE_DIGIT_PERCENT = false;
public static final boolean SHOW_100_PERCENT = false;
public static final int FULL = 96;
public static final int EMPTY = 4;
public static final float SUBPIXEL = 0.4f; // inset rects for softer edges
int[] mColors;
boolean mShowPercent = true;
Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
int mButtonHeight;
private float mTextHeight, mWarningTextHeight;
private int mHeight;
private int mWidth;
private String mWarningString;
private final int mChargeColor;
private final float[] mBoltPoints;
private final Path mBoltPath = new Path();
private final RectF mFrame = new RectF();
private final RectF mButtonFrame = new RectF();
private final RectF mClipFrame = new RectF();
private final RectF mBoltFrame = new RectF();
private class BatteryTracker extends BroadcastReceiver {
public static final int UNKNOWN_LEVEL = -1;
// current battery status
int level = UNKNOWN_LEVEL;
String percentStr;
int plugType;
boolean plugged;
int health;
int status;
String technology;
int voltage;
int temperature;
boolean testmode = false;
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性
if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
level = (int)(100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
plugged = plugType != 0;
health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
BatteryManager.BATTERY_HEALTH_UNKNOWN);
status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
setContentDescription(
context.getString(R.string.accessibility_battery_level, level));
postInvalidate();
} else if (action.equals(ACTION_LEVEL_TEST)) {
testmode = true;
post(new Runnable() {
int curLevel = 0;
int incr = 1;
int saveLevel = level;
int savePlugged = plugType;
Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
@Override
public void run() {
if (curLevel < 0) {
testmode = 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);
}
getContext().sendBroadcast(dummy);
if (!testmode) return;
curLevel += incr;
if (curLevel == 100) {
incr *= -1;
}
postDelayed(this, 200);
}
});
}
}
}
BatteryTracker mTracker = new BatteryTracker();
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(ACTION_LEVEL_TEST);
final Intent sticky = getContext().registerReceiver(mTracker, filter);
if (sticky != null) {
// preload the battery level
mTracker.onReceive(getContext(), sticky);
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
getContext().unregisterReceiver(mTracker);
}
public BatteryMeterView(Context context) {
this(context, null, 0);
}
public BatteryMeterView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
final int N = levels.length();
mColors = new int[2*N];
for (int i=0; i
mColors[2*i+1] = colors.getColor(i, 0);
}
levels.recycle();
colors.recycle();
mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
context.getContentResolver(), "status_bar_show_battery_percent", 0);
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));
mFramePaint.setDither(true);
mFramePaint.setStrokeWidth(0);
mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBatteryPaint.setDither(true);
mBatteryPaint.setStrokeWidth(0);
mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(0xFFFFFFFF);
Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
mTextPaint.setTypeface(font);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mWarningTextPaint.setColor(mColors[1]);
font = Typeface.create("sans-serif", Typeface.BOLD);
mWarningTextPaint.setTypeface(font);
mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);
mBoltPaint = new Paint();
mBoltPaint.setAntiAlias(true);
mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));
mBoltPoints = loadBoltPoints(res);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
private static float[] loadBoltPoints(Resources res) {
final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
int maxX = 0, maxY = 0;
for (int i = 0; i < pts.length; i += 2) {
maxX = Math.max(maxX, pts[i]);
maxY = Math.max(maxY, pts[i + 1]);
}
final float[] ptsF = new float[pts.length];
for (int i = 0; i < pts.length; i += 2) {
ptsF[i] = (float)pts[i] / maxX;
ptsF[i + 1] = (float)pts[i + 1] / maxY;
}
return ptsF;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mHeight = h;
mWidth = w;
mWarningTextPaint.setTextSize(h * 0.75f);
mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
}
private int getColorForLevel(int percent) {
int thresh, color = 0;
for (int i=0; i
color = mColors[i+1];
if (percent <= thresh) return color;
}
return color;
}
@Override
public void draw(Canvas c) { //通过电池属性绘制图标
BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
final int level = tracker.level;
if (level == BatteryTracker.UNKNOWN_LEVEL) return;
float drawFrac = (float) level / 100f;
final int pt = getPaddingTop();
final int pl = getPaddingLeft();
final int pr = getPaddingRight();
final int pb = getPaddingBottom();
int height = mHeight - pt - pb;
int width = mWidth - pl - pr;
mButtonHeight = (int) (height * 0.12f);
mFrame.set(0, 0, width, height);
mFrame.offset(pl, pt);
mButtonFrame.set( //这个就是电池图标上边的突起
mFrame.left + width * 0.25f,
mFrame.top,
mFrame.right - width * 0.25f,
mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);
mButtonFrame.top += SUBPIXEL;
mButtonFrame.left += SUBPIXEL;
mButtonFrame.right -= SUBPIXEL;
mFrame.top += mButtonHeight; //电池图标桶装图
mFrame.left += SUBPIXEL;
mFrame.top += SUBPIXEL;
mFrame.right -= SUBPIXEL;
mFrame.bottom -= SUBPIXEL;
// first, draw the battery shape
c.drawRect(mFrame, mFramePaint);
// fill 'er up
final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色
mBatteryPaint.setColor(color);
if (level >= FULL) { //96时,电池图标里面填充完整,满电。
drawFrac = 1f;
} else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量
drawFrac = 0f;
}
c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);
mClipFrame.set(mFrame);
mClipFrame.top += (mFrame.height() * (1f - drawFrac));
c.save(Canvas.CLIP_SAVE_FLAG);
c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释
c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)
c.restore();
if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电
// draw the bolt
final float bl = mFrame.left + mFrame.width() / 4.5f;
final float bt = mFrame.top + mFrame.height() / 6f;
final float br = mFrame.right - mFrame.width() / 7f;
final float bb = mFrame.bottom - mFrame.height() / 10f;
if (mBoltFrame.left != bl || mBoltFrame.top != bt
|| mBoltFrame.right != br || mBoltFrame.bottom != bb) {
mBoltFrame.set(bl, bt, br, bb);
mBoltPath.reset();
mBoltPath.moveTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
for (int i = 2; i < mBoltPoints.length; i += 2) {
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
}
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
}
c.drawPath(mBoltPath, mBoltPaint);
} else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号
final float x = mWidth * 0.5f;
final float y = (mHeight + mWarningTextHeight) * 0.48f;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
: (tracker.level == 100 ? 0.38f : 0.5f)));
mTextHeight = -mTextPaint.getFontMetrics().ascent;
final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
final float x = mWidth * 0.5f;
final float y = (mHeight + mTextHeight) * 0.47f;
c.drawText(str,
x,
y,
mTextPaint);
}
}
private boolean mDemoMode;
private BatteryTracker mDemoTracker = new BatteryTracker();
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoMode && command.equals(COMMAND_ENTER)) {
mDemoMode = true;
mDemoTracker.level = mTracker.level;
mDemoTracker.plugged = mTracker.plugged;
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
mDemoMode = false;
postInvalidate();
} else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
String level = args.getString("level");
String plugged = args.getString("plugged");
if (level != null) {
mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
if (plugged != null) {
mDemoTracker.plugged = Boolean.parseBoolean(plugged);
}
postInvalidate();
}
}
}
[java] view plain copy
/*
* Copyright (C) 2013 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.systemui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
public class BatteryMeterView extends View implements DemoMode {
public static final String TAG = BatteryMeterView.class.getSimpleName();
public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
public static final boolean ENABLE_PERCENT = true;
public static final boolean SINGLE_DIGIT_PERCENT = false;
public static final boolean SHOW_100_PERCENT = false;
public static final int FULL = 96;
public static final int EMPTY = 4;
public static final float SUBPIXEL = 0.4f; // inset rects for softer edges
int[] mColors;
boolean mShowPercent = true;
Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
int mButtonHeight;
private float mTextHeight, mWarningTextHeight;
private int mHeight;
private int mWidth;
private String mWarningString;
private final int mChargeColor;
private final float[] mBoltPoints;
private final Path mBoltPath = new Path();
private final RectF mFrame = new RectF();
private final RectF mButtonFrame = new RectF();
private final RectF mClipFrame = new RectF();
private final RectF mBoltFrame = new RectF();
private class BatteryTracker extends BroadcastReceiver {
public static final int UNKNOWN_LEVEL = -1;
// current battery status
int level = UNKNOWN_LEVEL;
String percentStr;
int plugType;
boolean plugged;
int health;
int status;
String technology;
int voltage;
int temperature;
boolean testmode = false;
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性
if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
level = (int)(100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
plugged = plugType != 0;
health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
BatteryManager.BATTERY_HEALTH_UNKNOWN);
status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
setContentDescription(
context.getString(R.string.accessibility_battery_level, level));
postInvalidate();
} else if (action.equals(ACTION_LEVEL_TEST)) {
testmode = true;
post(new Runnable() {
int curLevel = 0;
int incr = 1;
int saveLevel = level;
int savePlugged = plugType;
Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
@Override
public void run() {
if (curLevel < 0) {
testmode = 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);
}
getContext().sendBroadcast(dummy);
if (!testmode) return;
curLevel += incr;
if (curLevel == 100) {
incr *= -1;
}
postDelayed(this, 200);
}
});
}
}
}
BatteryTracker mTracker = new BatteryTracker();
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(ACTION_LEVEL_TEST);
final Intent sticky = getContext().registerReceiver(mTracker, filter);
if (sticky != null) {
// preload the battery level
mTracker.onReceive(getContext(), sticky);
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
getContext().unregisterReceiver(mTracker);
}
public BatteryMeterView(Context context) {
this(context, null, 0);
}
public BatteryMeterView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
final int N = levels.length();
mColors = new int[2*N];
for (int i=0; i
mColors[2*i+1] = colors.getColor(i, 0);
}
levels.recycle();
colors.recycle();
mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
context.getContentResolver(), "status_bar_show_battery_percent", 0);
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));
mFramePaint.setDither(true);
mFramePaint.setStrokeWidth(0);
mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBatteryPaint.setDither(true);
mBatteryPaint.setStrokeWidth(0);
mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(0xFFFFFFFF);
Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
mTextPaint.setTypeface(font);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mWarningTextPaint.setColor(mColors[1]);
font = Typeface.create("sans-serif", Typeface.BOLD);
mWarningTextPaint.setTypeface(font);
mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);
mBoltPaint = new Paint();
mBoltPaint.setAntiAlias(true);
mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));
mBoltPoints = loadBoltPoints(res);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
private static float[] loadBoltPoints(Resources res) {
final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
int maxX = 0, maxY = 0;
for (int i = 0; i < pts.length; i += 2) {
maxX = Math.max(maxX, pts[i]);
maxY = Math.max(maxY, pts[i + 1]);
}
final float[] ptsF = new float[pts.length];
for (int i = 0; i < pts.length; i += 2) {
ptsF[i] = (float)pts[i] / maxX;
ptsF[i + 1] = (float)pts[i + 1] / maxY;
}
return ptsF;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mHeight = h;
mWidth = w;
mWarningTextPaint.setTextSize(h * 0.75f);
mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
}
private int getColorForLevel(int percent) {
int thresh, color = 0;
for (int i=0; i
color = mColors[i+1];
if (percent <= thresh) return color;
}
return color;
}
@Override
public void draw(Canvas c) { //通过电池属性绘制图标
BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
final int level = tracker.level;
if (level == BatteryTracker.UNKNOWN_LEVEL) return;
float drawFrac = (float) level / 100f;
final int pt = getPaddingTop();
final int pl = getPaddingLeft();
final int pr = getPaddingRight();
final int pb = getPaddingBottom();
int height = mHeight - pt - pb;
int width = mWidth - pl - pr;
mButtonHeight = (int) (height * 0.12f);
mFrame.set(0, 0, width, height);
mFrame.offset(pl, pt);
mButtonFrame.set( //这个就是电池图标上边的突起
mFrame.left + width * 0.25f,
mFrame.top,
mFrame.right - width * 0.25f,
mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);
mButtonFrame.top += SUBPIXEL;
mButtonFrame.left += SUBPIXEL;
mButtonFrame.right -= SUBPIXEL;
mFrame.top += mButtonHeight; //电池图标桶装图
mFrame.left += SUBPIXEL;
mFrame.top += SUBPIXEL;
mFrame.right -= SUBPIXEL;
mFrame.bottom -= SUBPIXEL;
// first, draw the battery shape
c.drawRect(mFrame, mFramePaint);
// fill 'er up
final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色
mBatteryPaint.setColor(color);
if (level >= FULL) { //96时,电池图标里面填充完整,满电。
drawFrac = 1f;
} else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量
drawFrac = 0f;
}
c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);
mClipFrame.set(mFrame);
mClipFrame.top += (mFrame.height() * (1f - drawFrac));
c.save(Canvas.CLIP_SAVE_FLAG);
c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释
c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)
c.restore();
if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电
// draw the bolt
final float bl = mFrame.left + mFrame.width() / 4.5f;
final float bt = mFrame.top + mFrame.height() / 6f;
final float br = mFrame.right - mFrame.width() / 7f;
final float bb = mFrame.bottom - mFrame.height() / 10f;
if (mBoltFrame.left != bl || mBoltFrame.top != bt
|| mBoltFrame.right != br || mBoltFrame.bottom != bb) {
mBoltFrame.set(bl, bt, br, bb);
mBoltPath.reset();
mBoltPath.moveTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
for (int i = 2; i < mBoltPoints.length; i += 2) {
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
}
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
}
c.drawPath(mBoltPath, mBoltPaint);
} else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号
final float x = mWidth * 0.5f;
final float y = (mHeight + mWarningTextHeight) * 0.48f;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
: (tracker.level == 100 ? 0.38f : 0.5f)));
mTextHeight = -mTextPaint.getFontMetrics().ascent;
final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
final float x = mWidth * 0.5f;
final float y = (mHeight + mTextHeight) * 0.47f;
c.drawText(str,
x,
y,
mTextPaint);
}
}
private boolean mDemoMode;
private BatteryTracker mDemoTracker = new BatteryTracker();
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoMode && command.equals(COMMAND_ENTER)) {
mDemoMode = true;
mDemoTracker.level = mTracker.level;
mDemoTracker.plugged = mTracker.plugged;
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
mDemoMode = false;
postInvalidate();
} else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
String level = args.getString("level");
String plugged = args.getString("plugged");
if (level != null) {
mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
if (plugged != null) {
mDemoTracker.plugged = Boolean.parseBoolean(plugged);
}
postInvalidate();
}
}
}
总结:这个类就是绘制状态栏里面电量图标的自定义控件。
以上就是大概的逻辑,现总结,用于备忘。