android 4.4 batteryservice 电池电量显示分析

转载地址:http://blog.csdn.net/daweibalang717/article/details/40615453

最近工作接触到这么的东西,这是我对整个电池管理方面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   下面贴出源码。我把注释加上。个人理解,仅参考。

 

  1. /* 
  2.  * Copyright (C) 2006 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server;  
  18.   
  19. import android.os.BatteryStats;  
  20. import com.android.internal.app.IBatteryStats;  
  21. import com.android.server.am.BatteryStatsService;  
  22.   
  23. import android.app.ActivityManagerNative;  
  24. import android.content.ContentResolver;  
  25. import android.content.Context;  
  26. import android.content.Intent;  
  27. import android.content.pm.PackageManager;  
  28. import android.os.BatteryManager;  
  29. import android.os.BatteryProperties;  
  30. import android.os.Binder;  
  31. import android.os.FileUtils;  
  32. import android.os.Handler;  
  33. import android.os.IBatteryPropertiesListener;  
  34. import android.os.IBatteryPropertiesRegistrar;  
  35. import android.os.IBinder;  
  36. import android.os.DropBoxManager;  
  37. import android.os.RemoteException;  
  38. import android.os.ServiceManager;  
  39. import android.os.SystemClock;  
  40. import android.os.UEventObserver;  
  41. import android.os.UserHandle;  
  42. import android.provider.Settings;  
  43. import android.util.EventLog;  
  44. import android.util.Slog;  
  45.   
  46. import java.io.File;  
  47. import java.io.FileDescriptor;  
  48. import java.io.FileOutputStream;  
  49. import java.io.IOException;  
  50. import java.io.PrintWriter;  
  51.   
  52.   
  53. /** 
  54.  * <p>BatteryService monitors the charging status, and charge level of the device 
  55.  * battery.  When these values change this service broadcasts the new values 
  56.  * to all {@link android.content.BroadcastReceiver IntentReceivers} that are 
  57.  * watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED 
  58.  * BATTERY_CHANGED} action.</p> 
  59.  * <p>The new values are stored in the Intent data and can be retrieved by 
  60.  * calling {@link android.content.Intent#getExtra Intent.getExtra} with the 
  61.  * following keys:</p> 
  62.  * <p>"scale" - int, the maximum value for the charge level</p> 
  63.  * <p>"level" - int, charge level, from 0 through "scale" inclusive</p> 
  64.  * <p>"status" - String, the current charging status.<br /> 
  65.  * <p>"health" - String, the current battery health.<br /> 
  66.  * <p>"present" - boolean, true if the battery is present<br /> 
  67.  * <p>"icon-small" - int, suggested small icon to use for this state</p> 
  68.  * <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged 
  69.  * into an AC power adapter; 2 if plugged in via USB.</p> 
  70.  * <p>"voltage" - int, current battery voltage in millivolts</p> 
  71.  * <p>"temperature" - int, current battery temperature in tenths of 
  72.  * a degree Centigrade</p> 
  73.  * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> 
  74.  * 
  75.  * <p> 
  76.  * The battery service may be called by the power manager while holding its locks so 
  77.  * we take care to post all outcalls into the activity manager to a handler. 
  78.  * 
  79.  * FIXME: Ideally the power manager would perform all of its calls into the battery 
  80.  * service asynchronously itself. 
  81.  * </p> 
  82.  */  
  83. public final class BatteryService extends Binder {  
  84.     private static final String TAG = BatteryService.class.getSimpleName();  
  85.   
  86.     private static final boolean DEBUG = false;  
  87.   
  88.     private static final int BATTERY_SCALE = 100;    // battery capacity is a percentage  
  89.   
  90.     // Used locally for determining when to make a last ditch effort to log  
  91.     // discharge stats before the device dies.  
  92.     private int mCriticalBatteryLevel;  
  93.   
  94.     private static final int DUMP_MAX_LENGTH = 24 * 1024;  
  95.     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin""--unplugged" };  
  96.   
  97.     private static final String DUMPSYS_DATA_PATH = "/data/system/";  
  98.   
  99.     // This should probably be exposed in the API, though it's not critical  
  100.     private static final int BATTERY_PLUGGED_NONE = 0;  
  101.   
  102.     private final Context mContext;  
  103.     private final IBatteryStats mBatteryStats;  
  104.     private final Handler mHandler;  
  105.   
  106.     private final Object mLock = new Object();  
  107.   
  108.     private BatteryProperties mBatteryProps;  
  109.     private boolean mBatteryLevelCritical;  
  110.     private int mLastBatteryStatus;  
  111.     private int mLastBatteryHealth;  
  112.     private boolean mLastBatteryPresent;  
  113.     private int mLastBatteryLevel;  
  114.     private int mLastBatteryVoltage;  
  115.     private int mLastBatteryTemperature;  
  116.     private boolean mLastBatteryLevelCritical;  
  117.   
  118.     private int mInvalidCharger;  
  119.     private int mLastInvalidCharger;  
  120.   
  121.     private int mLowBatteryWarningLevel;  
  122.     private int mLowBatteryCloseWarningLevel;  
  123.     private int mShutdownBatteryTemperature;  
  124.   
  125.     private int mPlugType;  
  126.     private int mLastPlugType = -1// Extra state so we can detect first run  
  127.   
  128.     private long mDischargeStartTime;  
  129.     private int mDischargeStartLevel;  
  130.   
  131.     private boolean mUpdatesStopped;  
  132.   
  133.     private Led mLed;  
  134.   
  135.     private boolean mSentLowBatteryBroadcast = false;  
  136.   
  137.     private BatteryListener mBatteryPropertiesListener;  
  138.     private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;  
  139. //构造函数  
  140.     public BatteryService(Context context, LightsService lights) {  
  141.         mContext = context;  
  142.         mHandler = new Handler(true /*async*/);  
  143.         mLed = new Led(context, lights);//这个应该是指示灯,没实验  
  144.         mBatteryStats = BatteryStatsService.getService();  
  145.   
  146.  //低电量临界值,这个数我看的源码版本值是4(在这个类里只是用来写日志)  
  147.         mCriticalBatteryLevel = mContext.getResources().getInteger(  
  148.                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  149.   
  150. //低电量告警值,值15,下面会根据这个变量发送低电量的广播Intent.ACTION_BATTERY_LOW(这个跟系统低电量提醒没关系,只是发出去了)  
  151.         mLowBatteryWarningLevel = mContext.getResources().getInteger(  
  152.                 com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  153.   
  154. //电量告警取消值,值20 , 就是手机电量大于等于20的话发送Intent.ACTION_BATTERY_OKAY  
  155.         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(  
  156.                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  157.   
  158. //值是680 ,温度过高,超过这个值就发送广播,跳转到将要关机提醒。  
  159.        mShutdownBatteryTemperature = mContext.getResources().getInteger(  
  160.                 com.android.internal.R.integer.config_shutdownBatteryTemperature);  
  161.   
  162.         // watch for invalid charger messages if the invalid_charger switch exists  
  163.         if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {  
  164.             mInvalidChargerObserver.startObserving(  
  165.                     "DEVPATH=/devices/virtual/switch/invalid_charger");  
  166.         }  
  167. //电池监听,这个应该是注册到底层去了。当底层电量改变会调用此监听。然后执行update(BatteryProperties props);  
  168.         mBatteryPropertiesListener = new BatteryListener();  
  169.   
  170.         IBinder b = ServiceManager.getService("batterypropreg");  
  171.         mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b);  
  172.   
  173.         try {  
  174. //这里注册  
  175.           mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener);  
  176.         } catch (RemoteException e) {  
  177.             // Should never happen.  
  178.         }  
  179.     }  
  180. //开机后先去看看是否没电了或者温度太高了。如果是,就关机提示(关机提示我等会介绍)。  
  181.     void systemReady() {  
  182.         // check our power situation now that it is safe to display the shutdown dialog.  
  183.         synchronized (mLock) {  
  184.             shutdownIfNoPowerLocked();  
  185.             shutdownIfOverTempLocked();  
  186.         }  
  187.     }  
  188. //返回是否在充电,这个函数在PowerManagerService.java 中调用  
  189.     /** 
  190.      * Returns true if the device is plugged into any of the specified plug types. 
  191.      */  
  192.     public boolean isPowered(int plugTypeSet) {  
  193.         synchronized (mLock) {  
  194.             return isPoweredLocked(plugTypeSet);  
  195.         }  
  196.     }  
  197. //就是这里,通过充电器类型判断是否充电  
  198.     private boolean isPoweredLocked(int plugTypeSet) {  
  199. //我这英语小白猜着翻译下:就是开机后,电池状态不明了,那我们就认为就在充电,以便设备正常工作。  
  200.         // assume we are powered if battery state is unknown so  
  201.         // the "stay on while plugged in" option will work.  
  202.         if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {  
  203.             return true;  
  204.         }  
  205. //充电器  
  206.         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {  
  207.             return true;  
  208.         }  
  209. //USB,插电脑上充电  
  210.       if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {  
  211.             return true;  
  212.         }  
  213. //电源是无线的。 (我没见过...)  
  214.         if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {  
  215.             return true;  
  216.         }  
  217.         return false;  
  218.     }  
  219.   
  220.     /** 
  221.      * Returns the current plug type. 
  222.      */  
  223. //充电器类型  
  224.    public int getPlugType() {  
  225.         synchronized (mLock) {  
  226.             return mPlugType;  
  227.         }  
  228.     }  
  229.   
  230.     /** 
  231.      * Returns battery level as a percentage. 
  232.      */  
  233. //电池属性:电量等级(0-100)  
  234.     public int getBatteryLevel() {  
  235.         synchronized (mLock) {  
  236.             return mBatteryProps.batteryLevel;  
  237.         }  
  238.     }  
  239.   
  240.     /** 
  241.      * Returns true if battery level is below the first warning threshold. 
  242.      */  
  243. //低电量  
  244.      public boolean isBatteryLow() {  
  245.         synchronized (mLock) {  
  246.             return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;  
  247.         }  
  248.     }  
  249.   
  250.     /** 
  251.      * Returns a non-zero value if an  unsupported charger is attached. 
  252.      */  
  253. //不支持的充电器类型    
  254.     public int getInvalidCharger() {  
  255.         synchronized (mLock) {  
  256.             return mInvalidCharger;  
  257.         }  
  258.     }  
  259.   
  260. //这里就是没电了,要关机的提示。  
  261.     private void shutdownIfNoPowerLocked() {  
  262.         // shut down gracefully if our battery is critically low and we are not powered.  
  263.         // wait until the system has booted before attempting to display the shutdown dialog.  
  264.         if (mBatteryProps.batteryLevel == 0 && (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING)) {  
  265.             mHandler.post(new Runnable() {  
  266.                 @Override  
  267.                 public void run() {  
  268.                     if (ActivityManagerNative.isSystemReady()) {  
  269.                         Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
  270.                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  271.                         intent.putExtra("cant_be_cancel_by_button"true);  
  272.                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  273.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  274.                     }  
  275.                 }  
  276.             });  
  277.         }  
  278.     }  
  279.   
  280. //温度过高,关机提示(个人感觉这里有问题,温度过高为啥子跳转到没电关机提示界面)   
  281.     private void shutdownIfOverTempLocked() {  
  282.         // shut down gracefully if temperature is too high (> 68.0C by default)  
  283.         // wait until the system has booted before attempting to display the  
  284.         // shutdown dialog.  
  285.         if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {  
  286.             mHandler.post(new Runnable() {  
  287.                 @Override  
  288.                 public void run() {  
  289.                     if (ActivityManagerNative.isSystemReady()) {  
  290.                         Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY");//ACTION_REQUEST_SHUTDOWN  
  291.                         intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  292.                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  293.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  294.                     }  
  295.                 }  
  296.             });  
  297.         }  
  298.     }  
  299. //这个方法就是被JNI回调的。用来更新上层状态的方法。  
  300.     private void update(BatteryProperties props) {  
  301.         synchronized (mLock) {  
  302.             if (!mUpdatesStopped) {  
  303.                 mBatteryProps = props;  
  304.                 // Process the new values.  
  305.                 processValuesLocked();  
  306.             }  
  307.         }  
  308.     }  
  309. //嗯。这个就是最主要的方法了。  
  310.     private void processValuesLocked() {  
  311.         boolean logOutlier = false;  
  312.         long dischargeDuration = 0;  
  313.   
  314.         mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);  
  315. //充电器类型   
  316.        if (mBatteryProps.chargerAcOnline) {  
  317.             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;  
  318.         } else if (mBatteryProps.chargerUsbOnline) {  
  319.             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;  
  320.         } else if (mBatteryProps.chargerWirelessOnline) {  
  321.             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;  
  322.         } else {  
  323.             mPlugType = BATTERY_PLUGGED_NONE;  
  324.         }  
  325.   
  326.         if (DEBUG) {//日志,略过  
  327.             Slog.d(TAG, "Processing new values: "  
  328.                     + "chargerAcOnline=" + mBatteryProps.chargerAcOnline  
  329.                     + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline  
  330.                     + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline  
  331.                     + ", batteryStatus=" + mBatteryProps.batteryStatus  
  332.                     + ", batteryHealth=" + mBatteryProps.batteryHealth  
  333.                     + ", batteryPresent=" + mBatteryProps.batteryPresent  
  334.                     + ", batteryLevel=" + mBatteryProps.batteryLevel  
  335.                     + ", batteryTechnology=" + mBatteryProps.batteryTechnology  
  336.                     + ", batteryVoltage=" + mBatteryProps.batteryVoltage  
  337.                     + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow  
  338.                     + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter  
  339.                     + ", batteryTemperature=" + mBatteryProps.batteryTemperature  
  340.                     + ", mBatteryLevelCritical=" + mBatteryLevelCritical  
  341.                     + ", mPlugType=" + mPlugType);  
  342.         }  
  343.   
  344.         // Let the battery stats keep track of the current level.  
  345.         try {  
  346. //把电池属性放到状态里面  
  347.            mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,  
  348.                     mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,  
  349.                     mBatteryProps.batteryVoltage);  
  350.         } catch (RemoteException e) {  
  351.             // Should never happen.  
  352.         }  
  353. //没电了  
  354.         shutdownIfNoPowerLocked();  
  355. //温度过高了  
  356.        shutdownIfOverTempLocked();  
  357.   
  358.         if (mBatteryProps.batteryStatus != mLastBatteryStatus ||  
  359.                 mBatteryProps.batteryHealth != mLastBatteryHealth ||  
  360.                 mBatteryProps.batteryPresent != mLastBatteryPresent ||  
  361.                 mBatteryProps.batteryLevel != mLastBatteryLevel ||  
  362.                 mPlugType != mLastPlugType ||  
  363.                 mBatteryProps.batteryVoltage != mLastBatteryVoltage ||  
  364.                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||  
  365.                 mInvalidCharger != mLastInvalidCharger) {  
  366.   
  367.             if (mPlugType != mLastPlugType) {//当前充电器类型与上次的不一样  
  368. //并且上次充电器类型是no one ,那就可以知道,现在是插上充电器了。  
  369.                if (mLastPlugType == BATTERY_PLUGGED_NONE) {  
  370.                     // discharging -> charging  
  371.   
  372.                     // There's no value in this data unless we've discharged at least once and the  
  373.                     // battery level has changed; so don't log until it does.  
  374.                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {  
  375.                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
  376.                         logOutlier = true;  
  377.                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,  
  378.                                 mDischargeStartLevel, mBatteryProps.batteryLevel);  
  379.                         // make sure we see a discharge event before logging again  
  380.                         mDischargeStartTime = 0;  
  381.                     }  
  382. //并且本次充电器类型是no one ,那就可以知道,现在是拔掉充电器了。  
  383.                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {  
  384.                     // charging -> discharging or we just powered up  
  385.                     mDischargeStartTime = SystemClock.elapsedRealtime();  
  386.                     mDischargeStartLevel = mBatteryProps.batteryLevel;  
  387.                 }  
  388.             }  
  389.             if (mBatteryProps.batteryStatus != mLastBatteryStatus ||//写日志,略过  
  390.                     mBatteryProps.batteryHealth != mLastBatteryHealth ||  
  391.                     mBatteryProps.batteryPresent != mLastBatteryPresent ||  
  392.                     mPlugType != mLastPlugType) {  
  393.                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,  
  394.                         mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,  
  395.                         mPlugType, mBatteryProps.batteryTechnology);  
  396.             }  
  397.             if (mBatteryProps.batteryLevel != mLastBatteryLevel) {  
  398.                 // Don't do this just from voltage or temperature changes, that is  
  399.                 // too noisy.  
  400.                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,  
  401.                         mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);  
  402.             }  
  403.             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&  
  404.                     mPlugType == BATTERY_PLUGGED_NONE) {  
  405.                 // We want to make sure we log discharge cycle outliers  
  406.                 // if the battery is about to die.  
  407.                 dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;  
  408.                 logOutlier = true;  
  409.             }  
  410. //本次调用,当前的充电状态  
  411.             final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;  
  412. //本次调用,上次调用的充电状态    
  413.             final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;  
  414.   
  415.             /* The ACTION_BATTERY_LOW broadcast is sent in these situations: 
  416.              * - is just un-plugged (previously was plugged) and battery level is 
  417.              *   less than or equal to WARNING, or 
  418.              * - is not plugged and battery level falls to WARNING boundary 
  419.              *   (becomes <= mLowBatteryWarningLevel). 
  420.              */  
  421. //用于发送低电量广播的判断  
  422.             final boolean sendBatteryLow = !plugged//(按sendBatteryLow = true 来说) 当前没有充电  
  423.                     && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN//充电状态不是UNKNOWN  
  424.                     && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel//当前电量小于告警值 15  
  425.                     && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);//上次状态是充电或者上次电量等级大于告警值 15  
  426.   
  427.             sendIntentLocked();//发送电池电量改变的广播Intent.ACTION_BATTERY_CHANGED  
  428.   
  429.             // Separate broadcast is sent for power connected / not connected  
  430.             // since the standard intent will not wake any applications and some  
  431.             // applications may want to have smart behavior based on this.  
  432.             if (mPlugType != 0 && mLastPlugType == 0) {//插上充电器了  
  433.                 mHandler.post(new Runnable() {  
  434.                     @Override  
  435.                     public void run() {  
  436.                         Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);  
  437.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  438.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  439.                     }  
  440.                 });  
  441.             }  
  442.             else if (mPlugType == 0 && mLastPlugType != 0) {//断开充电器了  
  443.                 mHandler.post(new Runnable() {  
  444.                     @Override  
  445.                     public void run() {  
  446.                         Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);  
  447.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  448.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  449.                     }  
  450.                 });  
  451.             }  
  452. //发送低电量提醒(这个跟系统低电量提醒没关系,只是发出去了)  
  453.             if (sendBatteryLow) {  
  454.                 mSentLowBatteryBroadcast = true;  
  455.                 mHandler.post(new Runnable() {  
  456.                     @Override  
  457.                     public void run() {  
  458.                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);  
  459.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  460.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  461.                     }  
  462.                 });  
  463.             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {//电量超过20了。电池状态OK了  
  464.                 mSentLowBatteryBroadcast = false;  
  465.                 mHandler.post(new Runnable() {  
  466.                     @Override  
  467.                     public void run() {  
  468.                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);  
  469.                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  470.                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);  
  471.                     }  
  472.                 });  
  473.             }  
  474.   
  475.             // Update the battery LED  
  476.             mLed.updateLightsLocked();  
  477.   
  478.             // This needs to be done after sendIntent() so that we get the lastest battery stats.  
  479.             if (logOutlier && dischargeDuration != 0) {  
  480.                 logOutlierLocked(dischargeDuration);  
  481.             }  
  482.   
  483.             mLastBatteryStatus = mBatteryProps.batteryStatus;  
  484.             mLastBatteryHealth = mBatteryProps.batteryHealth;  
  485.             mLastBatteryPresent = mBatteryProps.batteryPresent;  
  486.             mLastBatteryLevel = mBatteryProps.batteryLevel;  
  487.             mLastPlugType = mPlugType;  
  488.             mLastBatteryVoltage = mBatteryProps.batteryVoltage;  
  489.             mLastBatteryTemperature = mBatteryProps.batteryTemperature;  
  490.             mLastBatteryLevelCritical = mBatteryLevelCritical;  
  491.             mLastInvalidCharger = mInvalidCharger;  
  492.         }  
  493.     }  
  494. //电池电量改变,把属性发出去(系统低电量提醒接收的是这个广播)  
  495.     private void sendIntentLocked() {  
  496.         //  Pack up the values and broadcast them to everyone  
  497.         final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);  
  498.         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
  499.                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  500.   
  501.         int icon = getIconLocked(mBatteryProps.batteryLevel);  
  502.   
  503.         intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);  
  504.         intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);  
  505.         intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);  
  506.         intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);  
  507.         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);  
  508.         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);  
  509.         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);  
  510.         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);  
  511.         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  
  512.         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);  
  513.         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);  
  514.   
  515.         if (DEBUG) {  
  516.             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +  
  517.                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +  
  518.                     ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +  
  519.                     ", voltage: " + mBatteryProps.batteryVoltage +  
  520.                     ", temperature: " + mBatteryProps.batteryTemperature +  
  521.                     ", technology: " + mBatteryProps.batteryTechnology +  
  522.                     ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +  
  523.                     ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +  
  524.                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);  
  525.         }  
  526.   
  527.         mHandler.post(new Runnable() {  
  528.             @Override  
  529.             public void run() {  
  530.                 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);  
  531.             }  
  532.         });  
  533.     }  
  534.   
  535.     private void logBatteryStatsLocked() {  
  536.         IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME);  
  537.         if (batteryInfoService == nullreturn;  
  538.   
  539.         DropBoxManager db = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);  
  540.         if (db == null || !db.isTagEnabled("BATTERY_DISCHARGE_INFO")) return;  
  541.   
  542.         File dumpFile = null;  
  543.         FileOutputStream dumpStream = null;  
  544.         try {  
  545.             // dump the service to a file  
  546.             dumpFile = new File(DUMPSYS_DATA_PATH + BatteryStats.SERVICE_NAME + ".dump");  
  547.             dumpStream = new FileOutputStream(dumpFile);  
  548.             batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);  
  549.             FileUtils.sync(dumpStream);  
  550.   
  551.             // add dump file to drop box  
  552.             db.addFile("BATTERY_DISCHARGE_INFO", dumpFile, DropBoxManager.IS_TEXT);  
  553.         } catch (RemoteException e) {  
  554.             Slog.e(TAG, "failed to dump battery service", e);  
  555.         } catch (IOException e) {  
  556.             Slog.e(TAG, "failed to write dumpsys file", e);  
  557.         } finally {  
  558.             // make sure we clean up  
  559.             if (dumpStream != null) {  
  560.                 try {  
  561.                     dumpStream.close();  
  562.                 } catch (IOException e) {  
  563.                     Slog.e(TAG, "failed to close dumpsys output stream");  
  564.                 }  
  565.             }  
  566.             if (dumpFile != null && !dumpFile.delete()) {  
  567.                 Slog.e(TAG, "failed to delete temporary dumpsys file: "  
  568.                         + dumpFile.getAbsolutePath());  
  569.             }  
  570.         }  
  571.     }  
  572.   
  573.     private void logOutlierLocked(long duration) {  
  574.         ContentResolver cr = mContext.getContentResolver();  
  575.         String dischargeThresholdString = Settings.Global.getString(cr,  
  576.                 Settings.Global.BATTERY_DISCHARGE_THRESHOLD);  
  577.         String durationThresholdString = Settings.Global.getString(cr,  
  578.                 Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD);  
  579.   
  580.         if (dischargeThresholdString != null && durationThresholdString != null) {  
  581.             try {  
  582.                 long durationThreshold = Long.parseLong(durationThresholdString);  
  583.                 int dischargeThreshold = Integer.parseInt(dischargeThresholdString);  
  584.                 if (duration <= durationThreshold &&  
  585.                         mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {  
  586.                     // If the discharge cycle is bad enough we want to know about it.  
  587.                     logBatteryStatsLocked();  
  588.                 }  
  589.                 if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +  
  590.                         " discharge threshold: " + dischargeThreshold);  
  591.                 if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +  
  592.                         (mDischargeStartLevel - mBatteryProps.batteryLevel));  
  593.             } catch (NumberFormatException e) {  
  594.                 Slog.e(TAG, "Invalid DischargeThresholds GService string: " +  
  595.                         durationThresholdString + " or " + dischargeThresholdString);  
  596.                 return;  
  597.             }  
  598.         }  
  599.     }  
  600.   
  601.     private int getIconLocked(int level) {  
  602.         if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {  
  603.             return com.android.internal.R.drawable.stat_sys_battery_charge;  
  604.         } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {  
  605.             return com.android.internal.R.drawable.stat_sys_battery;  
  606.         } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING  
  607.                 || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {  
  608.             if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)  
  609.                     && mBatteryProps.batteryLevel >= 100) {  
  610.                 return com.android.internal.R.drawable.stat_sys_battery_charge;  
  611.             } else {  
  612.                 return com.android.internal.R.drawable.stat_sys_battery;  
  613.             }  
  614.         } else {  
  615.             return com.android.internal.R.drawable.stat_sys_battery_unknown;  
  616.         }  
  617.     }  
  618.   
  619.     @Override  
  620.     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
  621.         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)  
  622.                 != PackageManager.PERMISSION_GRANTED) {  
  623.   
  624.             pw.println("Permission Denial: can't dump Battery service from from pid="  
  625.                     + Binder.getCallingPid()  
  626.                     + ", uid=" + Binder.getCallingUid());  
  627.             return;  
  628.         }  
  629.   
  630.         synchronized (mLock) {  
  631.             if (args == null || args.length == 0 || "-a".equals(args[0])) {  
  632.                 pw.println("Current Battery Service state:");  
  633.                 if (mUpdatesStopped) {  
  634.                     pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");  
  635.                 }  
  636.                 pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);  
  637.                 pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);  
  638.                 pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);  
  639.                 pw.println("  status: " + mBatteryProps.batteryStatus);  
  640.                 pw.println("  health: " + mBatteryProps.batteryHealth);  
  641.                 pw.println("  present: " + mBatteryProps.batteryPresent);  
  642.                 pw.println("  level: " + mBatteryProps.batteryLevel);  
  643.                 pw.println("  scale: " + BATTERY_SCALE);  
  644.                 pw.println("  voltage: " + mBatteryProps.batteryVoltage);  
  645.   
  646.                 if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {  
  647.                     pw.println("  current now: " + mBatteryProps.batteryCurrentNow);  
  648.                 }  
  649.   
  650.                 if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {  
  651.                     pw.println("  charge counter: " + mBatteryProps.batteryChargeCounter);  
  652.                 }  
  653.   
  654.                 pw.println("  temperature: " + mBatteryProps.batteryTemperature);  
  655.                 pw.println("  technology: " + mBatteryProps.batteryTechnology);  
  656.             } else if (args.length == 3 && "set".equals(args[0])) {  
  657.                 String key = args[1];  
  658.                 String value = args[2];  
  659.                 try {  
  660.                     boolean update = true;  
  661.                     if ("ac".equals(key)) {  
  662.                         mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;  
  663.                     } else if ("usb".equals(key)) {  
  664.                         mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;  
  665.                     } else if ("wireless".equals(key)) {  
  666.                         mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;  
  667.                     } else if ("status".equals(key)) {  
  668.                         mBatteryProps.batteryStatus = Integer.parseInt(value);  
  669.                     } else if ("level".equals(key)) {  
  670.                         mBatteryProps.batteryLevel = Integer.parseInt(value);  
  671.                     } else if ("invalid".equals(key)) {  
  672.                         mInvalidCharger = Integer.parseInt(value);  
  673.                     } else {  
  674.                         pw.println("Unknown set option: " + key);  
  675.                         update = false;  
  676.                     }  
  677.                     if (update) {  
  678.                         long ident = Binder.clearCallingIdentity();  
  679.                         try {  
  680.                             mUpdatesStopped = true;  
  681.                             processValuesLocked();  
  682.                         } finally {  
  683.                             Binder.restoreCallingIdentity(ident);  
  684.                         }  
  685.                     }  
  686.                 } catch (NumberFormatException ex) {  
  687.                     pw.println("Bad value: " + value);  
  688.                 }  
  689.             } else if (args.length == 1 && "reset".equals(args[0])) {  
  690.                 long ident = Binder.clearCallingIdentity();  
  691.                 try {  
  692.                     mUpdatesStopped = false;  
  693.                 } finally {  
  694.                     Binder.restoreCallingIdentity(ident);  
  695.                 }  
  696.             } else {  
  697.                 pw.println("Dump current battery state, or:");  
  698.                 pw.println("  set ac|usb|wireless|status|level|invalid <value>");  
  699.                 pw.println("  reset");  
  700.             }  
  701.         }  
  702.     }  
  703.   
  704.     private final UEventObserver mInvalidChargerObserver = new UEventObserver() {  
  705.         @Override  
  706.         public void onUEvent(UEventObserver.UEvent event) {  
  707.             final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;  
  708.             synchronized (mLock) {  
  709.                 if (mInvalidCharger != invalidCharger) {  
  710.                     mInvalidCharger = invalidCharger;  
  711.                 }  
  712.             }  
  713.         }  
  714.     };  
  715.   
  716.     private final class Led {  
  717.         private final LightsService.Light mBatteryLight;  
  718.   
  719.         private final int mBatteryLowARGB;  
  720.         private final int mBatteryMediumARGB;  
  721.         private final int mBatteryFullARGB;  
  722.         private final int mBatteryLedOn;  
  723.         private final int mBatteryLedOff;  
  724.   
  725.         public Led(Context context, LightsService lights) {  
  726.             mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);  
  727.   
  728.             mBatteryLowARGB = context.getResources().getInteger(  
  729.                     com.android.internal.R.integer.config_notificationsBatteryLowARGB);  
  730.             mBatteryMediumARGB = context.getResources().getInteger(  
  731.                     com.android.internal.R.integer.config_notificationsBatteryMediumARGB);  
  732.             mBatteryFullARGB = context.getResources().getInteger(  
  733.                     com.android.internal.R.integer.config_notificationsBatteryFullARGB);  
  734.             mBatteryLedOn = context.getResources().getInteger(  
  735.                     com.android.internal.R.integer.config_notificationsBatteryLedOn);  
  736.             mBatteryLedOff = context.getResources().getInteger(  
  737.                     com.android.internal.R.integer.config_notificationsBatteryLedOff);  
  738.         }  
  739.   
  740.         /** 
  741.          * Synchronize on BatteryService. 
  742.          */  
  743.         public void updateLightsLocked() {  
  744.             final int level = mBatteryProps.batteryLevel;  
  745.             final int status = mBatteryProps.batteryStatus;  
  746.             if (level < mLowBatteryWarningLevel) {  
  747.                 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {  
  748.                     // Solid red when battery is charging  
  749.                     mBatteryLight.setColor(mBatteryLowARGB);  
  750.                 } else {  
  751.                     // Flash red when battery is low and not charging  
  752.                     mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,  
  753.                             mBatteryLedOn, mBatteryLedOff);  
  754.                 }  
  755.             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING  
  756.                     || status == BatteryManager.BATTERY_STATUS_FULL) {  
  757.                 if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {  
  758.                     // Solid green when full or charging and nearly full  
  759.                     mBatteryLight.setColor(mBatteryFullARGB);  
  760.                 } else {  
  761.                     // Solid orange when charging and halfway full  
  762.                     mBatteryLight.setColor(mBatteryMediumARGB);  
  763.                 }  
  764.             } else {  
  765.                 // No lights if not charging and not low  
  766.                 mBatteryLight.turnOff();  
  767.             }  
  768.         }  
  769.     }  
  770.   
  771.     private final class BatteryListener extends IBatteryPropertiesListener.Stub {  
  772.         public void batteryPropertiesChanged(BatteryProperties props) {  
  773.             BatteryService.this.update(props);  
  774.        }  
  775.     }  
  776. }  
/*
 * 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
    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 <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);

            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

  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.systemui.power;  
  18.   
  19. import android.app.AlertDialog;  
  20. import android.content.BroadcastReceiver;  
  21. import android.content.ContentResolver;  
  22. import android.content.Context;  
  23. import android.content.DialogInterface;  
  24. import android.content.Intent;  
  25. import android.content.IntentFilter;  
  26. import android.media.AudioManager;  
  27. import android.media.Ringtone;  
  28. import android.media.RingtoneManager;  
  29. import android.net.Uri;  
  30. import android.os.BatteryManager;  
  31. import android.os.Handler;  
  32. import android.os.PowerManager;  
  33. import android.os.SystemClock;  
  34. import android.os.UserHandle;  
  35. import android.provider.Settings;  
  36. import android.util.Slog;  
  37. import android.view.View;  
  38. import android.view.WindowManager;  
  39. import android.widget.TextView;  
  40.   
  41. import com.android.systemui.R;  
  42. import com.android.systemui.SystemUI;  
  43.   
  44. import java.io.FileDescriptor;  
  45. import java.io.PrintWriter;  
  46. import java.util.Arrays;  
  47.   
  48. public class PowerUI extends SystemUI { //总体说一下,这里才是处理低电量提醒的地方,他接收的广播是Intent.ACTION_BATTERY_CHANGED  
  49.     static final String TAG = "PowerUI";  
  50.   
  51.     static final boolean DEBUG = false;  
  52.   
  53.     Handler mHandler = new Handler();  
  54.   
  55.     int mBatteryLevel = 100;  
  56.     int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;  
  57.     int mPlugType = 0;  
  58.     int mInvalidCharger = 0;  
  59.   
  60.     int mLowBatteryAlertCloseLevel;  
  61.     int[] mLowBatteryReminderLevels = new int[2];  
  62.   
  63.     AlertDialog mInvalidChargerDialog;  
  64.     AlertDialog mLowBatteryDialog;  
  65.     TextView mBatteryLevelTextView;  
  66.   
  67.     private long mScreenOffTime = -1;  
  68.   
  69.     public void start() {  //这个类会在手机启动后,在SystemUI 里启动(就是系统界面)。  
  70.   
  71.         mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( //电量告警取消值,值20   
  72.                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);  
  73.         mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( //低电量提醒 15  
  74.                 com.android.internal.R.integer.config_lowBatteryWarningLevel);  
  75.         mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( //低电量临界值 4  
  76.                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);  
  77.   
  78.         final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
  79.         mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();  
  80.   
  81.         // Register for Intent broadcasts for...  
  82.         IntentFilter filter = new IntentFilter();  
  83.         filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  84.         filter.addAction(Intent.ACTION_SCREEN_OFF);  
  85.         filter.addAction(Intent.ACTION_SCREEN_ON);  
  86.         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
  87.     }  
  88.   
  89.     /** 
  90.      * Buckets the battery level. 
  91.      * 
  92.      * The code in this function is a little weird because I couldn't comprehend 
  93.      * the bucket going up when the battery level was going down. --joeo 
  94.      * 
  95.      * 1 means that the battery is "ok" 
  96.      * 0 means that the battery is between "ok" and what we should warn about. 
  97.      * less than 0 means that the battery is low 
  98.      */  
  99.     private int findBatteryLevelBucket(int level) {   //这个方法是用来警告判断用的。  
  100.         if (level >= mLowBatteryAlertCloseLevel) {  
  101.             return 1;  
  102.         }  
  103.         if (level >= mLowBatteryReminderLevels[0]) {  
  104.             return 0;  
  105.         }  
  106.         final int N = mLowBatteryReminderLevels.length;  
  107.         for (int i=N-1; i>=0; i--) {  
  108.             if (level <= mLowBatteryReminderLevels[i]) {  
  109.                 return -1-i;  
  110.             }  
  111.         }  
  112.         throw new RuntimeException("not possible!");  
  113.     }  
  114.   
  115.     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //重点,广播接收处理  
  116.         @Override  
  117.         public void onReceive(Context context, Intent intent) {  
  118.             String action = intent.getAction();  
  119.             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {  //电池电量改变  
  120.                 final int oldBatteryLevel = mBatteryLevel; //下边就是根据Intent获取BatteryService传过来的电池属性  
  121.                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100); /  
  122.                 final int oldBatteryStatus = mBatteryStatus;  
  123.                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
  124.                         BatteryManager.BATTERY_STATUS_UNKNOWN);  
  125.                 final int oldPlugType = mPlugType;  
  126.                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);  
  127.                 final int oldInvalidCharger = mInvalidCharger;  
  128.                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);  
  129.   
  130.                 final boolean plugged = mPlugType != 0;  
  131.                 final boolean oldPlugged = oldPlugType != 0;  
  132.   
  133.                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel); //这两个值特别有意思,就是说记录下老的电量,记录一下新的电量,比较电量是增加了,还是减小了  
  134.                 int bucket = findBatteryLevelBucket(mBatteryLevel);  
  135.   
  136.                 if (DEBUG) {  
  137.                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel  
  138.                             + " .. " + mLowBatteryReminderLevels[0]  
  139.                             + " .. " + mLowBatteryReminderLevels[1]);  
  140.                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);  
  141.                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);  
  142.                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);  
  143.                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);  
  144.                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);  
  145.                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);  
  146.                 }  
  147.   
  148.                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {  
  149.                     Slog.d(TAG, "showing invalid charger warning");  
  150.                     showInvalidChargerDialog(); //就是充电器不识别的弹窗  
  151.                     return;  
  152.                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {  
  153.                     dismissInvalidChargerDialog();  
  154.                 } else if (mInvalidChargerDialog != null) {  
  155.                     // if invalid charger is showing, don't show low battery  
  156.                     return;  
  157.                 }  
  158.   
  159.                 if (!plugged  
  160.                         && (bucket < oldBucket || oldPlugged)  
  161.                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN  
  162.                         && bucket < 0) {  
  163.                     showLowBatteryWarning(); <span style="color:#FF0000;">//这里哈,低电量提醒的弹窗</span>  
  164.   
  165.                     // only play SFX when the dialog comes up or the bucket changes  
  166.                     if (bucket != oldBucket || oldPlugged) {  
  167.                         playLowBatterySound();  
  168.                     }  
  169.                 } else if (plugged || (bucket > oldBucket && bucket > 0)) { //插上充电器或者充电电池电量超过20取消弹窗  
  170.                     dismissLowBatteryWarning();  
  171.                 } else if (mBatteryLevelTextView != null) {  
  172.                     showLowBatteryWarning();  
  173.                 }  
  174.             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {  
  175.                 mScreenOffTime = SystemClock.elapsedRealtime();  
  176.             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {  
  177.                 mScreenOffTime = -1;  
  178.             } else {  
  179.                 Slog.w(TAG, "unknown intent: " + intent);  
  180.             }  
  181.         }  
  182.     };  
  183.   
  184.     void dismissLowBatteryWarning() {  
  185.         if (mLowBatteryDialog != null) {  
  186.             Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);  
  187.             mLowBatteryDialog.dismiss();  
  188.         }  
  189.     }  
  190.   
  191.     void showLowBatteryWarning() {  
  192.         Slog.i(TAG,  
  193.                 ((mBatteryLevelTextView == null) ? "showing" : "updating")  
  194.                 + " low battery warning: level=" + mBatteryLevel  
  195.                 + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");  
  196.   
  197.         CharSequence levelText = mContext.getString(  
  198.                 R.string.battery_low_percent_format, mBatteryLevel);  
  199.   
  200.         if (mBatteryLevelTextView != null) {  
  201.             mBatteryLevelTextView.setText(levelText);  
  202.         } else {  
  203.             View v = View.inflate(mContext, R.layout.battery_low, null);  
  204.             mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);  
  205.   
  206.             mBatteryLevelTextView.setText(levelText);  
  207.   
  208.             AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
  209.                 b.setCancelable(true);  
  210.                 b.setTitle(R.string.battery_low_title);  
  211.                 b.setView(v);  
  212.                 b.setIconAttribute(android.R.attr.alertDialogIcon);  
  213.                 b.setPositiveButton(android.R.string.ok, null);  
  214.   
  215.             final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);  
  216.             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK  
  217.                     | Intent.FLAG_ACTIVITY_MULTIPLE_TASK  
  218.                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  
  219.                     | Intent.FLAG_ACTIVITY_NO_HISTORY);  
  220.             if (intent.resolveActivity(mContext.getPackageManager()) != null) {  
  221.                 b.setNegativeButton(R.string.battery_low_why,  
  222.                         new DialogInterface.OnClickListener() {  
  223.                     @Override  
  224.                     public void onClick(DialogInterface dialog, int which) {  
  225.                         mContext.startActivityAsUser(intent, UserHandle.CURRENT);  
  226.                         dismissLowBatteryWarning();  
  227.                     }  
  228.                 });  
  229.             }  
  230.   
  231.             AlertDialog d = b.create();  
  232.             d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
  233.                     @Override  
  234.                     public void onDismiss(DialogInterface dialog) {  
  235.                         mLowBatteryDialog = null;  
  236.                         mBatteryLevelTextView = null;  
  237.                     }  
  238.                 });  
  239.             d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  240.             d.getWindow().getAttributes().privateFlags |=  
  241.                     WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;  
  242.             d.show();  
  243.             mLowBatteryDialog = d;  
  244.         }  
  245.     }  
  246.   
  247.     void playLowBatterySound() {  
  248.         final ContentResolver cr = mContext.getContentResolver();  
  249.   
  250.         final int silenceAfter = Settings.Global.getInt(cr,  
  251.                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);  
  252.         final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;  
  253.         if (silenceAfter > 0  
  254.                 && mScreenOffTime > 0  
  255.                 && offTime > silenceAfter) {  
  256.             Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter  
  257.                     + "ms): not waking up the user with low battery sound");  
  258.             return;  
  259.         }  
  260.   
  261.         if (DEBUG) {  
  262.             Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated  
  263.         }  
  264.   
  265.         if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {  
  266.             final String soundPath = Settings.Global.getString(cr,  
  267.                     Settings.Global.LOW_BATTERY_SOUND);  
  268.             if (soundPath != null) {  
  269.                 final Uri soundUri = Uri.parse("file://" + soundPath);  
  270.                 if (soundUri != null) {  
  271.                     final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);  
  272.                     if (sfx != null) {  
  273.                         sfx.setStreamType(AudioManager.STREAM_SYSTEM);  
  274.                         sfx.play();  
  275.                     }  
  276.                 }  
  277.             }  
  278.         }  
  279.     }  
  280.   
  281.     void dismissInvalidChargerDialog() {  
  282.         if (mInvalidChargerDialog != null) {  
  283.             mInvalidChargerDialog.dismiss();  
  284.         }  
  285.     }  
  286.   
  287.     void showInvalidChargerDialog() {  
  288.         Slog.d(TAG, "showing invalid charger dialog");  
  289.   
  290.         dismissLowBatteryWarning();  
  291.   
  292.         AlertDialog.Builder b = new AlertDialog.Builder(mContext);  
  293.             b.setCancelable(true);  
  294.             b.setMessage(R.string.invalid_charger);  
  295.             b.setIconAttribute(android.R.attr.alertDialogIcon);  
  296.             b.setPositiveButton(android.R.string.ok, null);  
  297.   
  298.         AlertDialog d = b.create();  
  299.             d.setOnDismissListener(new DialogInterface.OnDismissListener() {  
  300.                     public void onDismiss(DialogInterface dialog) {  
  301.                         mInvalidChargerDialog = null;  
  302.                         mBatteryLevelTextView = null;  
  303.                     }  
  304.                 });  
  305.   
  306.         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  307.         d.show();  
  308.         mInvalidChargerDialog = d;  
  309.     }  
  310.   
  311.     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {  
  312.         pw.print("mLowBatteryAlertCloseLevel=");  
  313.         pw.println(mLowBatteryAlertCloseLevel);  
  314.         pw.print("mLowBatteryReminderLevels=");  
  315.         pw.println(Arrays.toString(mLowBatteryReminderLevels));  
  316.         pw.print("mInvalidChargerDialog=");  
  317.         pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());  
  318.         pw.print("mLowBatteryDialog=");  
  319.         pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());  
  320.         pw.print("mBatteryLevel=");  
  321.         pw.println(Integer.toString(mBatteryLevel));  
  322.         pw.print("mBatteryStatus=");  
  323.         pw.println(Integer.toString(mBatteryStatus));  
  324.         pw.print("mPlugType=");  
  325.         pw.println(Integer.toString(mPlugType));  
  326.         pw.print("mInvalidCharger=");  
  327.         pw.println(Integer.toString(mInvalidCharger));  
  328.         pw.print("mScreenOffTime=");  
  329.         pw.print(mScreenOffTime);  
  330.         if (mScreenOffTime >= 0) {  
  331.             pw.print(" (");  
  332.             pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);  
  333.             pw.print(" ago)");  
  334.         }  
  335.         pw.println();  
  336.         pw.print("soundTimeout=");  
  337.         pw.println(Settings.Global.getInt(mContext.getContentResolver(),  
  338.                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));  
  339.         pw.print("bucket: ");  
  340.         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));  
  341.     }  
  342. }  
/*
 * 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(); <span style="color:#FF0000;">//这里哈,低电量提醒的弹窗</span>

                    // 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):

  1.  <activity android:name="com.android.server.ShutdownLowBatteryActivity"  
  2.     android:theme="@android:style/Theme.Translucent.NoTitleBar"  
  3.     android:configChanges="orientation|keyboardHidden|screenSize"  
  4.     android:windowSoftInputMode="stateHidden"  
  5.     android:permission="android.permission.SHUTDOWN"  
  6.     android:excludeFromRecents="true">  
  7.     <intent-filter>  
  8.         <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY" />  
  9.         <category android:name="android.intent.category.DEFAULT" />  
  10.     </intent-filter>  
  11. </activity>  
         <activity android:name="com.android.server.ShutdownLowBatteryActivity"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:windowSoftInputMode="stateHidden"
            android:permission="android.permission.SHUTDOWN"
            android:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN_LOWBATTERY" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>


android:theme="@android:style/Theme.Translucent.NoTitleBar"API小于11的透明主题。这样弹出的dialog会是老版本的主题。(这个Activity是透明的)

对于Theme,因为是4.4,我更倾向于:android:theme="@android:style/Theme.Holo.Panel"


ShutdownLowBatteryActivity.java:

  1. /* 
  2.  * Copyright (C) 2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.server;  
  18.   
  19. import android.app.Activity;  
  20. import android.app.AlertDialog;  
  21. import android.app.KeyguardManager;  
  22. import android.content.BroadcastReceiver;  
  23. import android.content.Context;  
  24. import android.content.DialogInterface;  
  25. import android.content.Intent;  
  26. import android.content.IntentFilter;  
  27. import android.os.Bundle;  
  28. import android.os.Handler;  
  29. import android.util.Slog;  
  30. import android.view.Window;  
  31. import android.view.WindowManager;  
  32. import android.view.View;  
  33.   
  34.   
  35. //import com.android.internal.app.ShutdownThread;  
  36. import com.android.server.power.ShutdownThread;  
  37.   
  38. import android.telephony.TelephonyManager;  
  39. import android.telephony.PhoneStateListener;  
  40. import android.media.MediaPlayer;  
  41. import android.media.MediaPlayer.OnCompletionListener;  
  42. import android.content.ContentResolver;  
  43. import android.provider.Settings;  
  44.   
  45. import java.io.IOException;  
  46.   
  47. public class ShutdownLowBatteryActivity extends Activity {  
  48.   
  49.     private static final String TAG = "ShutdownLowBatteryActivity";  
  50.     private boolean mConfirm;  
  51.     private int mSeconds = 15;//电池电量等于0 15秒内不插充电器就自动关机  
  52.     private AlertDialog mDialog;  
  53.     private Handler myHandler = new Handler();  
  54.     private Runnable myRunnable = new Runnable() { //这里数秒关机  
  55.         @Override  
  56.         public void run() {  
  57.             mSeconds --;  
  58.             if(mSeconds <1)  
  59.                 mSeconds=0;  
  60.             mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  61.   
  62.             if(mSeconds <= 1){  
  63.                 myHandler.removeCallbacks(myRunnable);  
  64.                 Handler h = new Handler();  
  65.                 h.post(new Runnable() {  
  66.                     public void run() {  
  67.                         ShutdownThread.shutdown(ShutdownLowBatteryActivity.this, mConfirm);  
  68.                     }  
  69.                 });  
  70.             }  
  71.             myHandler.postDelayed(myRunnable,1000);  
  72.         }  
  73.     };  
  74.   
  75.     private BroadcastReceiver mReceiver;  
  76.     private MediaPlayer mplayer;  
  77.   
  78.     @Override  
  79.     protected void onCreate(Bundle savedInstanceState) {  
  80.         super.onCreate(savedInstanceState);  
  81.   
  82.         mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  83.         Slog.i(TAG, "onCreate(): confirm=" + mConfirm);  
  84.    
  85.         //if(getIntent().getBooleanExtra("can_be_cancel", false)) { //这行注释掉了: 然后当连上充电器后或者电量涨到20的时候,就取消倒计时关机  
  86.                 mReceiver = new BroadcastReceiver() {  
  87.                 @Override  
  88.                 public void onReceive(Context context, Intent intent) {  
  89.                     if(Intent.ACTION_BATTERY_OKAY.equals(intent.getAction())|  
  90.                         Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())){  
  91.                         ShutDownWakeLock.releaseCpuLock();  
  92.                         myHandler.removeCallbacks(myRunnable);  
  93.                         if(mReceiver != null)  
  94.                             unregisterReceiver(mReceiver);  
  95.                         finish();  
  96.                     }  
  97.                 }  
  98.             };  
  99.   
  100.          IntentFilter filter=new IntentFilter(Intent.ACTION_POWER_CONNECTED);  
  101.          filter.addAction(Intent.ACTION_BATTERY_OKAY);  
  102.          registerReceiver(mReceiver, filter);  
  103.          //}  
  104.    
  105.   
  106.         PhoneStateListener mPhoneStateListener = new PhoneStateListener() { //如果正数秒呢,电话呼入了。取消自动关机  
  107.             @Override  
  108.             public void onCallStateChanged(int state, String ignored) {  
  109.                 if (state == TelephonyManager.CALL_STATE_RINGING) {  
  110.                     ShutDownWakeLock.releaseCpuLock();  
  111.                     myHandler.removeCallbacks(myRunnable);  
  112.                     finish();  
  113.                 }  
  114.             }  
  115.         };  
  116.   
  117.         TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
  118.         mTelephonyManager.listen(mPhoneStateListener,  
  119.                 PhoneStateListener.LISTEN_CALL_STATE);  
  120.         requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);  
  121.         Window win = getWindow();  
  122.         win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED  
  123.                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);  
  124.         win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
  125.                     | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);  
  126.   
  127.         setContentView(new View(this));  
  128.         mDialog=new AlertDialog.Builder(this).create();  
  129.         mDialog.setTitle(com.android.internal.R.string.low_battery_shutdown_title);  
  130.         mDialog.setMessage(getString(com.android.internal.R.string.low_battery_shutdown_after_seconds,mSeconds));  
  131.         if(!getIntent().getBooleanExtra("cant_be_cancel_by_button"false)) {//读取配置文件,是否运行取消,然后根据这个显示取消自动关机按钮  
  132.             mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,getText(com.android.internal.R.string.cancel), new DialogInterface.OnClickListener() {  
  133.                     @Override  
  134.                     public void onClick(DialogInterface dialog, int which) {  
  135.                         myHandler.removeCallbacks(myRunnable);  
  136.                         dialog.cancel();  
  137.                         if(mReceiver != null)  
  138.                             unregisterReceiver(mReceiver);  
  139.                         finish();  
  140.                     }});  
  141.         }  
  142.         mDialog.setCancelable(false);  
  143.         //mDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  144.         mDialog.show();  
  145.         if(mConfirm == false){  
  146.             myHandler.postDelayed(myRunnable, 1000);  
  147.         }  
  148.         myHandler.post(new Runnable(){  
  149.             public void run(){  
  150.                 final ContentResolver cr = getContentResolver();  
  151.                 String path=Settings.System.getString(cr,Settings.System.NOTIFICATION_SOUND);  
  152.   
  153.                mplayer=new MediaPlayer();  
  154.                 try{  
  155.                     mplayer.reset();  
  156.                     mplayer.setDataSource("system/media/audio/ui/LowBattery.ogg");                    
  157.                     mplayer.prepare();  
  158.                     mplayer.start();  
  159.                     mplayer.setOnCompletionListener(new OnCompletionListener() {  
  160.                         @Override  
  161.                         public void onCompletion(MediaPlayer mp) {  
  162.                             if(null != mplayer){  
  163.                                 mplayer.stop();  
  164.                                 mplayer.release();  
  165.                                 mplayer = null;  
  166.                             }  
  167.                         }  
  168.                     });  
  169.                 }  
  170.                 catch(IOException e){  
  171.                       
  172.                 }  
  173.             }  
  174.         });  
  175.     }  
  176. }  
/*
 * 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

这个类是自定义控件,定义在手机状态栏里:

  1. /* 
  2.  * Copyright (C) 2013 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.android.systemui;  
  18.   
  19. import android.content.BroadcastReceiver;  
  20. import android.content.Context;  
  21. import android.content.Intent;  
  22. import android.content.IntentFilter;  
  23. import android.content.res.Resources;  
  24. import android.content.res.TypedArray;  
  25. import android.graphics.Canvas;  
  26. import android.graphics.Paint;  
  27. import android.graphics.Path;  
  28. import android.graphics.PorterDuff;  
  29. import android.graphics.PorterDuffXfermode;  
  30. import android.graphics.Rect;  
  31. import android.graphics.RectF;  
  32. import android.graphics.Typeface;  
  33. import android.os.BatteryManager;  
  34. import android.os.Bundle;  
  35. import android.provider.Settings;  
  36. import android.util.AttributeSet;  
  37. import android.view.View;  
  38.   
  39. public class BatteryMeterView extends View implements DemoMode {  
  40.     public static final String TAG = BatteryMeterView.class.getSimpleName();  
  41.     public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";  
  42.   
  43.     public static final boolean ENABLE_PERCENT = true;  
  44.     public static final boolean SINGLE_DIGIT_PERCENT = false;  
  45.     public static final boolean SHOW_100_PERCENT = false;  
  46.   
  47.     public static final int FULL = 96;  
  48.     public static final int EMPTY = 4;  
  49.   
  50.     public static final float SUBPIXEL = 0.4f;  // inset rects for softer edges  
  51.   
  52.     int[] mColors;  
  53.   
  54.     boolean mShowPercent = true;  
  55.     Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;  
  56.     int mButtonHeight;  
  57.     private float mTextHeight, mWarningTextHeight;  
  58.   
  59.     private int mHeight;  
  60.     private int mWidth;  
  61.     private String mWarningString;  
  62.     private final int mChargeColor;  
  63.     private final float[] mBoltPoints;  
  64.     private final Path mBoltPath = new Path();  
  65.   
  66.     private final RectF mFrame = new RectF();  
  67.     private final RectF mButtonFrame = new RectF();  
  68.     private final RectF mClipFrame = new RectF();  
  69.     private final RectF mBoltFrame = new RectF();  
  70.   
  71.     private class BatteryTracker extends BroadcastReceiver {  
  72.         public static final int UNKNOWN_LEVEL = -1;  
  73.   
  74.         // current battery status  
  75.         int level = UNKNOWN_LEVEL;  
  76.         String percentStr;  
  77.         int plugType;  
  78.         boolean plugged;  
  79.         int health;  
  80.         int status;  
  81.         String technology;  
  82.         int voltage;  
  83.         int temperature;  
  84.         boolean testmode = false;  
  85.   
  86.         @Override  
  87.         public void onReceive(Context context, Intent intent) {  
  88.             final String action = intent.getAction();  
  89.             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { //接收电量改变的广播,取电池状态的属性  
  90.                 if (testmode && ! intent.getBooleanExtra("testmode"false)) return;  
  91.   
  92.                 level = (int)(100f  
  93.                         * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)  
  94.                         / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));  
  95.   
  96.                 plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);  
  97.                 plugged = plugType != 0;  
  98.                 health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,  
  99.                         BatteryManager.BATTERY_HEALTH_UNKNOWN);  
  100.                 status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,  
  101.                         BatteryManager.BATTERY_STATUS_UNKNOWN);  
  102.                 technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);  
  103.                 voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);  
  104.                 temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);  
  105.   
  106.                 setContentDescription(  
  107.                         context.getString(R.string.accessibility_battery_level, level));  
  108.                 postInvalidate();  
  109.             } else if (action.equals(ACTION_LEVEL_TEST)) {  
  110.                 testmode = true;  
  111.                 post(new Runnable() {  
  112.                     int curLevel = 0;  
  113.                     int incr = 1;  
  114.                     int saveLevel = level;  
  115.                     int savePlugged = plugType;  
  116.                     Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);  
  117.                     @Override  
  118.                     public void run() {  
  119.                         if (curLevel < 0) {  
  120.                             testmode = false;  
  121.                             dummy.putExtra("level", saveLevel);  
  122.                             dummy.putExtra("plugged", savePlugged);  
  123.                             dummy.putExtra("testmode"false);  
  124.                         } else {  
  125.                             dummy.putExtra("level", curLevel);  
  126.                             dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);  
  127.                             dummy.putExtra("testmode"true);  
  128.                         }  
  129.                         getContext().sendBroadcast(dummy);  
  130.   
  131.                         if (!testmode) return;  
  132.   
  133.                         curLevel += incr;  
  134.                         if (curLevel == 100) {  
  135.                             incr *= -1;  
  136.                         }  
  137.                         postDelayed(this200);  
  138.                     }  
  139.                 });  
  140.             }  
  141.         }  
  142.     }  
  143.   
  144.     BatteryTracker mTracker = new BatteryTracker();  
  145.   
  146.     @Override  
  147.     public void onAttachedToWindow() {  
  148.         super.onAttachedToWindow();  
  149.   
  150.         IntentFilter filter = new IntentFilter();  
  151.         filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  152.         filter.addAction(ACTION_LEVEL_TEST);  
  153.         final Intent sticky = getContext().registerReceiver(mTracker, filter);  
  154.         if (sticky != null) {  
  155.             // preload the battery level  
  156.             mTracker.onReceive(getContext(), sticky);  
  157.         }  
  158.     }  
  159.   
  160.     @Override  
  161.     public void onDetachedFromWindow() {  
  162.         super.onDetachedFromWindow();  
  163.   
  164.         getContext().unregisterReceiver(mTracker);  
  165.     }  
  166.   
  167.     public BatteryMeterView(Context context) {  
  168.         this(context, null0);  
  169.     }  
  170.   
  171.     public BatteryMeterView(Context context, AttributeSet attrs) {  
  172.         this(context, attrs, 0);  
  173.     }  
  174.   
  175.     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {  
  176.         super(context, attrs, defStyle);  
  177.   
  178.         final Resources res = context.getResources();  
  179.         TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);  
  180.         TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);  
  181.   
  182.         final int N = levels.length();  
  183.         mColors = new int[2*N];  
  184.         for (int i=0; i<N; i++) {  
  185.             mColors[2*i] = levels.getInt(i, 0);  
  186.             mColors[2*i+1] = colors.getColor(i, 0);  
  187.         }  
  188.         levels.recycle();  
  189.         colors.recycle();  
  190.         mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(  
  191.                 context.getContentResolver(), "status_bar_show_battery_percent"0);  
  192.   
  193.         mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);  
  194.   
  195.         mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  196.         mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));  
  197.         mFramePaint.setDither(true);  
  198.         mFramePaint.setStrokeWidth(0);  
  199.         mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  200.         mFramePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));  
  201.   
  202.         mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  203.         mBatteryPaint.setDither(true);  
  204.         mBatteryPaint.setStrokeWidth(0);  
  205.         mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  206.   
  207.         mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  208.         mTextPaint.setColor(0xFFFFFFFF);  
  209.         Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);  
  210.         mTextPaint.setTypeface(font);  
  211.         mTextPaint.setTextAlign(Paint.Align.CENTER);  
  212.   
  213.         mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  214.         mWarningTextPaint.setColor(mColors[1]);  
  215.         font = Typeface.create("sans-serif", Typeface.BOLD);  
  216.         mWarningTextPaint.setTypeface(font);  
  217.         mWarningTextPaint.setTextAlign(Paint.Align.CENTER);  
  218.   
  219.         mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);  
  220.   
  221.         mBoltPaint = new Paint();  
  222.         mBoltPaint.setAntiAlias(true);  
  223.         mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));  
  224.         mBoltPoints = loadBoltPoints(res);  
  225.         setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  226.     }  
  227.   
  228.     private static float[] loadBoltPoints(Resources res) {  
  229.         final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);  
  230.         int maxX = 0, maxY = 0;  
  231.         for (int i = 0; i < pts.length; i += 2) {  
  232.             maxX = Math.max(maxX, pts[i]);  
  233.             maxY = Math.max(maxY, pts[i + 1]);  
  234.         }  
  235.         final float[] ptsF = new float[pts.length];  
  236.         for (int i = 0; i < pts.length; i += 2) {  
  237.             ptsF[i] = (float)pts[i] / maxX;  
  238.             ptsF[i + 1] = (float)pts[i + 1] / maxY;  
  239.         }  
  240.         return ptsF;  
  241.     }  
  242.   
  243.     @Override  
  244.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  245.         mHeight = h;  
  246.         mWidth = w;  
  247.         mWarningTextPaint.setTextSize(h * 0.75f);  
  248.         mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;  
  249.     }  
  250.   
  251.     private int getColorForLevel(int percent) {  
  252.         int thresh, color = 0;  
  253.         for (int i=0; i<mColors.length; i+=2) {  
  254.             thresh = mColors[i];  
  255.             color = mColors[i+1];  
  256.             if (percent <= thresh) return color;  
  257.         }  
  258.         return color;  
  259.     }  
  260.   
  261.     @Override  
  262.     public void draw(Canvas c) {  //通过电池属性绘制图标  
  263.         BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;  
  264.         final int level = tracker.level;  
  265.   
  266.         if (level == BatteryTracker.UNKNOWN_LEVEL) return;  
  267.   
  268.         float drawFrac = (float) level / 100f;  
  269.         final int pt = getPaddingTop();  
  270.         final int pl = getPaddingLeft();  
  271.         final int pr = getPaddingRight();  
  272.         final int pb = getPaddingBottom();  
  273.         int height = mHeight - pt - pb;  
  274.         int width = mWidth - pl - pr;  
  275.   
  276.         mButtonHeight = (int) (height * 0.12f);  
  277.   
  278.         mFrame.set(00, width, height);  
  279.         mFrame.offset(pl, pt);  
  280.   
  281.         mButtonFrame.set(  //这个就是电池图标上边的突起  
  282.                 mFrame.left + width * 0.25f,  
  283.                 mFrame.top,  
  284.                 mFrame.right - width * 0.25f,  
  285.                 mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);  
  286.   
  287.         mButtonFrame.top += SUBPIXEL;  
  288.         mButtonFrame.left += SUBPIXEL;  
  289.         mButtonFrame.right -= SUBPIXEL;  
  290.   
  291.         mFrame.top += mButtonHeight; //电池图标桶装图  
  292.         mFrame.left += SUBPIXEL;  
  293.         mFrame.top += SUBPIXEL;  
  294.         mFrame.right -= SUBPIXEL;  
  295.         mFrame.bottom -= SUBPIXEL;  
  296.   
  297.         // first, draw the battery shape  
  298.         c.drawRect(mFrame, mFramePaint);  
  299.   
  300.         // fill 'er up  
  301.         final int color = tracker.plugged ? mChargeColor : getColorForLevel(level); //根据等级获取颜色,如果是15以下,就是红色。15以上是白色  
  302.         mBatteryPaint.setColor(color);  
  303.   
  304.         if (level >= FULL) { //96时,电池图标里面填充完整,满电。  
  305.             drawFrac = 1f;  
  306.         } else if (level <= EMPTY) { //这个变量是说,当电量小于4的时候,电池图标里面没有填充色,空电量  
  307.             drawFrac = 0f;  
  308.         }  
  309.   
  310.         c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);  
  311.   
  312.         mClipFrame.set(mFrame);  
  313.         mClipFrame.top += (mFrame.height() * (1f - drawFrac));  
  314.   
  315.         c.save(Canvas.CLIP_SAVE_FLAG);  
  316.         c.clipRect(mClipFrame);//在mFrame区域中保持mClipFrame的区域不变,参考下句注释  
  317.         c.drawRect(mFrame, mBatteryPaint);//在mFrame中,把除去mClipFrame的区域绘制。(电量状态,如果是15以下,就是红色。15以上是白色)  
  318.         c.restore();  
  319.   
  320.         if (tracker.plugged) { //这里就犀利了。这是说插上充电器就画一个闪电  
  321.             // draw the bolt  
  322.             final float bl = mFrame.left + mFrame.width() / 4.5f;  
  323.             final float bt = mFrame.top + mFrame.height() / 6f;  
  324.             final float br = mFrame.right - mFrame.width() / 7f;  
  325.             final float bb = mFrame.bottom - mFrame.height() / 10f;  
  326.             if (mBoltFrame.left != bl || mBoltFrame.top != bt  
  327.                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {  
  328.                 mBoltFrame.set(bl, bt, br, bb);  
  329.                 mBoltPath.reset();  
  330.                 mBoltPath.moveTo(  
  331.                         mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
  332.                         mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
  333.                 for (int i = 2; i < mBoltPoints.length; i += 2) {  
  334.                     mBoltPath.lineTo(  
  335.                             mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),  
  336.                             mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());  
  337.                 }  
  338.                 mBoltPath.lineTo(  
  339.                         mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),  
  340.                         mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());  
  341.             }  
  342.             c.drawPath(mBoltPath, mBoltPaint);  
  343.         } else if (level <= EMPTY) { //这个是空电量的时候,画一个红色的感叹号  
  344.             final float x = mWidth * 0.5f;  
  345.             final float y = (mHeight + mWarningTextHeight) * 0.48f;  
  346.             c.drawText(mWarningString, x, y, mWarningTextPaint);  
  347.         } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { //以文字显示电量。不过这个没有用到(除非mShowPercent变量获取的配置属性为true)  
  348.             mTextPaint.setTextSize(height *  
  349.                     (SINGLE_DIGIT_PERCENT ? 0.75f  
  350.                             : (tracker.level == 100 ? 0.38f : 0.5f)));  
  351.             mTextHeight = -mTextPaint.getFontMetrics().ascent;  
  352.   
  353.             final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);  
  354.             final float x = mWidth * 0.5f;  
  355.             final float y = (mHeight + mTextHeight) * 0.47f;  
  356.             c.drawText(str,  
  357.                     x,  
  358.                     y,  
  359.                     mTextPaint);  
  360.         }  
  361.     }  
  362.   
  363.     private boolean mDemoMode;  
  364.     private BatteryTracker mDemoTracker = new BatteryTracker();  
  365.   
  366.     @Override  
  367.     public void dispatchDemoCommand(String command, Bundle args) {  
  368.         if (!mDemoMode && command.equals(COMMAND_ENTER)) {  
  369.             mDemoMode = true;  
  370.             mDemoTracker.level = mTracker.level;  
  371.             mDemoTracker.plugged = mTracker.plugged;  
  372.         } else if (mDemoMode && command.equals(COMMAND_EXIT)) {  
  373.             mDemoMode = false;  
  374.             postInvalidate();  
  375.         } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {  
  376.            String level = args.getString("level");  
  377.            String plugged = args.getString("plugged");  
  378.            if (level != null) {  
  379.                mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);  
  380.            }  
  381.            if (plugged != null) {  
  382.                mDemoTracker.plugged = Boolean.parseBoolean(plugged);  
  383.            }  
  384.            postInvalidate();  
  385.         }  
  386.     }  
  387. }  
/*
 * 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<N; i++) {
            mColors[2*i] = levels.getInt(i, 0);
            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<mColors.length; i+=2) {
            thresh = mColors[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();
        }
    }
}


总结:这个类就是绘制状态栏里面电量图标的自定义控件。


以上就是大概的逻辑,现总结,用于备忘。


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值