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