电池电量分析 从上层到底层


本文将从底层到上层介绍android系统中电量显示这一块,电池检测采用的是ADC采样,不是使用市场上封装好的电量计芯片;

1.驱动层

    linux内核中提供power_supply_core.c电池管理核心,我们驱动程序主要完成核心给我们提供的接口:battery.get_property,在这个程序中,我们主要获得电池的电压、是否正常、容量、充放电状态等;通过这些值来改变文件系统中/sys/class/power_supply/目录下的文件值,下面主要介绍以上几个参数的获取方法:

    1)电池充放电状态gBatChargeStatus:

        当DC插入时,会产生一个中断,在这个中断处理程序中,我们主要完成给全局变量gBatChargeStatus赋值,如果DC插入,则gBatChargeStatus=1,否则为0,同事,在中断处理程序中,调用send_wakeup_key()唤醒触摸屏;

    2)电池状态

        电池状态主要有:充电、放电、电量满三种状态;当DC没插入时,电池处于放电状态,当DC插入时,如果电池电池芯片没有报电量满切电量达到100%则为充电状态,否则为充电状态

    3)电压值

        电压值采用ADC模块对电池电压的采样,获得电池的电压值,因为系统在使用过程中可能存在跳变,所以,对电池电压的获得最好采取多次采样取平均值的方法。在通过adc读电压值的时候,可以采取同步读或者异步读,区别是异步读在第二次执行这个程序的时候获取第一次执行的结果,这样做的好处就是效率高点;

    4)电量值

        电量值是用户实际看的的值,是用户最关心的,也是最重要的值,在处理电量值的时候注意,充电状态下电量值不能降低,放电状态下电量值不能升高,而且电量值不能大弧度跨越,基本是按照1%这样的变化;特别要注意的是在低电量和电量接近100%的时候的处理。

        电量值主要是根据测得的电压值获取,在开始写驱动前,得获得两组数据,分别是充电和没充电下没10%对应的电压值,切两组数据需要对应上,不如在电量为50%的时候,如果没接DC测得的电压值可能为3700而接着DC测得的电压值可能为3800,所以每一个百分比最好都能对应上。

    获取这些值后,驱动里每隔50ms就会调用一次比较函数,确认以上的几个值是否改变,如果改变了,则调用power_supply_changed()->kobject->uevent()上报uevent事件。同事电池管理核心就会调用接口battery.get_property更新/sys/class/power_supply目录下的值拱上层调用。

2.获取电池状态值

BatteryService作为电池及充电相关的服务,它的实现非常简单:监听UEvent,读取sysfs里中的状态。实现了一个UEvent的观察者,uevent是Linux内核用来向用户空间主动上报事件的机制,对于JAVA程序来说,只实现UEventObserver的虚函数onUEvent,然后注册即可。

private UEventObserver mUEventObserver = newUEventObserver() {
         @Override
         public voidonUEvent(UEventObserver.UEvent event) {
         String strOnline =event.get("POWER_SUPPLY_ONLINE");  //电源连接状态
         String strBatteryState =event.get("POWER_SUPPLY_STATUS");

         //电池状态,"Discharging","Charging","Notcharging","Full","Unknown"
         String strBatteryLevel =event.get("POWER_SUPPLY_CAPACITY");//电池容量
            ...
         }
   }

 1).BatteryService通过JNI(com_android_server_BatteryService.cpp)读取数据。BatteryService通过JNI注册的不仅有函数,还有变量。 如下:
   //##############在BatteryService.java中声明的变量################
   private boolean mAcOnline;
     private boolean mUsbOnline;
   private int mBatteryStatus;
   private int mBatteryHealth;
   private boolean mBatteryPresent;
   private int mBatteryLevel;
   private int mBatteryVoltage;
   private int mBatteryTemperature;
   private String mBatteryTechnology;

 2).在BatteryService.java中声明的变量,在com_android_server_BatteryService.cpp中共用,即在com_android_server_BatteryService.cpp中其实操作的也是BatteryService.java中声明的变量
   gFieldIds.mAcOnline = env->GetFieldID(clazz,"mAcOnline", "Z");
   gFieldIds.mUsbOnline = env->GetFieldID(clazz,"mUsbOnline", "Z");
   gFieldIds.mBatteryStatus = env->GetFieldID(clazz,"mBatteryStatus", "I");
   gFieldIds.mBatteryHealth = env->GetFieldID(clazz,"mBatteryHealth", "I");
   gFieldIds.mBatteryPresent = env->GetFieldID(clazz,"mBatteryPresent", "Z");
   gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel","I");
   gFieldIds.mBatteryTechnology = env->GetFieldID(clazz,"mBatteryTechnology", "Ljava/lang/String;");
   gFieldIds.mBatteryVoltage = env->GetFieldID(clazz,"mBatteryVoltage", "I");
     gFieldIds.mBatteryTemperature=env->GetFieldID(clazz, "mBatteryTemperature", "I");

 3).上面这些变量的值,对应是从下面的文件中读取的,一个文件存储一个数值。
    #define AC_ONLINE_PATH"/sys/class/power_supply/ac/online"  AC电源连接状态 

    #defineUSB_ONLINE_PATH "/sys/class/power_supply/usb/online"USB电源连接状
  #defineBATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"充电状
   #define BATTERY_HEALTH_PATH"/sys/class/power_supply/battery/health"电池状
   #define BATTERY_PRESENT_PATH"/sys/class/power_supply/battery/present"状态
    #define BATTERY_CAPACITY_PATH"/sys/class/power_supply/battery/capacity"   电池容量
    #define BATTERY_VOLTAGE_PATH"/sys/class/power_supply/battery/batt_vol"    电池电压
  #defineBATTERY_TEMPERATURE_PATH"/sys/class/power_supply/battery/batt_temp"   电池温度
  #defineBATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"   电池技术

update然后根据读到的状态更新BatteryService的成员变量,并广播一个Intent来通知其它关注电源状态的组件。

private final void sendIntent() {
        // Pack up the values and broadcast them to everyone
        Intent intent = newIntent(Intent.ACTION_BATTERY_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        try {
           mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE,mBatteryLevel);
        } catch (RemoteException e) {
            // Should never happen.
        }
        int icon = getIcon(mBatteryLevel);
        intent.putExtra("status",mBatteryStatus);
        intent.putExtra("health",mBatteryHealth);
        intent.putExtra("present",mBatteryPresent);
        intent.putExtra("level",mBatteryLevel);

...........
        ActivityManagerNative.broadcastStickyIntent(intent,null);
    }

3.根据电池状态值改变UI

关注ACTION_BATTERY_CHANGED的地方有好几个:
1) KeyguardUpdateMonitor 这里主要是用来更新锁屏界面下的电池状态和低电关机

    private void handleBatteryUpdate(intpluggedInStatus, int batteryLevel) {
        if (DEBUG) Log.d(TAG,"handleBatteryUpdate");
        final boolean pluggedIn =isPluggedIn(pluggedInStatus);

        if(isBatteryUpdateInteresting(pluggedIn, batteryLevel)) {
            mBatteryLevel = batteryLevel;
            mDevicePluggedIn = pluggedIn;
            for (int i = 0; i < mInfoCallbacks.size();i++) {
               mInfoCallbacks.get(i).onRefreshBatteryInfo(
                       shouldShowBatteryInfo(), pluggedIn, batteryLevel);
            }
        }
        // shut down gracefully if our batteryis critically low and we are not powered
        if (batteryLevel == 0 &&
                pluggedInStatus !=BATTERY_STATUS_CHARGING &&
                pluggedInStatus !=BATTERY_STATUS_UNKNOWN) {
           ShutdownThread.shutdownAfterDisablingRadio(mContext, false);
        }
    }

2 )NotificationManagerService 用来更新充电状态(LED)
      if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
          boolean batteryCharging =(intent.getIntExtra("plugged", 0) != 0);
          int level =intent.getIntExtra("level", -1);
          boolean batteryLow = (level >= 0&& level <=    Power.LOW_BATTERY_THRESHOLD);
          int status =intent.getIntExtra("status",   BatteryManager.BATTERY_STATUS_UNKNOWN);
          boolean batteryFull = (status ==BatteryManager.BATTERY_STATUS_FULL || level >= 90);
          if (batteryCharging !=mBatteryCharging ||
                    batteryLow != mBatteryLow||
                    batteryFull !=mBatteryFull) {
                    mBatteryCharging =batteryCharging;
                    mBatteryLow = batteryLow;
                    mBatteryFull = batteryFull;
                    updateLights();
                }
            }

3 )PowerManagerService 这里主要是做两件事件,先是检查是否在充电时不允许睡眠,并采用相应的行动,其次是触发一个用户行为(会影响下一次睡眠的时间)。

    private final class BatteryReceiver extendsBroadcastReceiver {
        @Override
        public void onReceive(Context context,Intent intent) {
            synchronized (mLocks) {
                boolean wasPowered =mIsPowered;
                mIsPowered = mBatteryService.isPowered();
                if (mIsPowered != wasPowered) {
                    updateWakeLockLocked();.
                    synchronized (mLocks) {
                        booleansavedActivityAllowed = mUserActivityAllowed;
                        mUserActivityAllowed =true;
                       userActivity(SystemClock.uptimeMillis(), false);
                        mUserActivityAllowed =savedActivityAllowed;
                    }
                }
            }
        }
    }

4 )LocationManagerService 这里似乎没有什么用处,我没找到mCollector赋值的地方。
             if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
               log("PowerStateBroadcastReceiver: Battery changed");
                synchronized(mLocationListeners) {
                    int scale =intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
                    int level =intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
                    boolean plugged =intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
                    if (mCollector != null) {
                       mCollector.updateBatteryState(scale, level, plugged);
                    }
                }
            }

5 )WifiService 根据电源状态来决定是否需要定时唤醒
if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
            int pluggedType =intent.getIntExtra("plugged", 0);
        if (mScreenOff &&shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&!shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
            long triggerTime =System.currentTimeMillis() + idleMillis;
            mAlARMManager.set(AlARMManager.RTC_WAKEUP,triggerTime, mIdleIntent);
            mPluggedType = pluggedType;
            return;
            }
            mPluggedType = pluggedType;
            }

6 )StatusBarPolicy用来更新状态栏上的充电图标。
    if (action.equals(Intent.ACTION_BATTERY_CHANGED)){
              updateBattery(intent);
            }
7)发送低电警报
首先需要判断是否需要发送低电量信息:Intent.ACTION_BATTERY_LOW

      final boolean sendBatteryLow = !plugged && mBatteryStatus !=BatteryManager.BATTERY_STATUS_UNKNOWN

       &&mBatteryLevel <= mLowBatteryWarningLevel

       &&(oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);

可以看到发送低电量信息会有两个条件,

1)当前不在充电状态,上次update时处于充电状态,并且电池电量小于等于mLowBatteryWarningLevel(低电量警告值)

2)当前不在充电状态,电池电量小于等于mLowBatteryWarningLevel(低电量警告值),并且上次update时,电量大于mLowBatteryWarningLevel(低电量警告值)

下面代码是具体发送

   if(sendBatteryLow) {

      mSentLowBatteryBroadcast = true;

      statusIntent.setAction(Intent.ACTION_BATTERY_LOW);

      mContext.sendBroadcast(statusIntent);

       }else if(mSentLowBatteryBroadcast && mLastBatteryLevel >=mLowBatteryCloseWarningLevel) {

      mSentLowBatteryBroadcast = false;

      statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);

      mContext.sendBroadcast(statusIntent);

       }

 

 

你可能感兴趣的:(电池电量分析 从上层到底层)