android 2.3 电量管理

2024-04-16 07:58
文章标签 android 管理 2.3 电量

本文主要是介绍android 2.3 电量管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.     介绍.... 4

2.     电池管理.... 4

2.1.      FrameWork.. 5

2.1.1.       监听... 5

2.1.1.1.     UEventObserver的实现... 6

2.1.1.2.     uevent 8

2.1.2.       状态读取... 9

2.1.3.       更新显示... 9

2.2.      驱动... 11

3.     用电统计.... 20


1.       介绍

              首先区分一下电源管理, 电池管理和用电统计三个概念。

l         电源管理:前一篇文章讲的是电源管理, 电源管理的目的是节电, 让设备在空闲的时间进入睡眠状态。

l         电池管理: 电池管理的目的是对电池电量和状态的管理, 比如电量变化时和进入充电状态时更新任务栏上的进度条, 当系统低电量时通知用户, 电量低到一定程度时自动关机等。

l         用电统计:用电统计的目的则是统计系统中一些模块, 服务和应用程序的耗电情况, 并反馈给用户。

       这篇文章是关于电池管理和用电统计的。

2.       电池管理

       Android电池管理的功能其实很简单:

l         监测电池电量的变化并更新显示界面

l         监听进入充电状态和退出充电状态消息并更新界面

2.1.      FrameWork

       



2.1.1.          监听

       在BatteryService定义了UEventObserver, uevent是Linux内核用来向用户空间主动上报事件的机制,关于uevent请参见2.1.1.2节, JAVA中的UEventObserver就用来监听uevent的。

private UEventObserver mUEventObserver = new UEventObserver() {

        @Override

        public void onUEvent(UEventObserver.UEvent event) {

            update();

        }

};

然后调用mUEventObserver.startObserving("SUBSYSTEM=power_supply")只监听属性SUBSYSTEM 为“power_supply”的消息。 也就是说当系统进入充电状态和退出充电状态时, 电量变化时就会调用到这个onUEvent函数来处理变化。

2.1.1.1.    UEventObserver的实现

      UEventObserver的实现分为三层,

UEventObserver.java->android_os_UEventObserver.cpp->hardware\libhardware_legacy\uevent\uevent.c

l         API层是com.android.os.UEventObserver

l         JNI层是android_os_UEventObserver.cpp, 很浅

l         Hal层是hardware\libhardware_legacy\uevent\uevent.c, 直接和内核通信

 

       当第一次调用mUEventObserver.startObserving会启动一个UEventThread, next_event会返回监听到的uevent事件, 然后再判断是不是我们所关心的, 如果是就会回调我们注册的onUEvent函数。如下:

private static class UEventThread extends Thread {

        public void run() {

            native_setup();

 

            byte[] buffer = new byte[1024];

            int len;

            while (true) {

                len = next_event(buffer);

                if (len > 0) {

                    String bufferStr = new String(buffer, 0, len);  // easier to search a String

                    synchronized (mObservers) {

                        for (int i = 0; i < mObservers.size(); i += 2) {

                            if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {

                                ((UEventObserver)mObservers.get(i+1))

                                        .onUEvent(new UEvent(bufferStr));

                            }

                        }

                    }

                }

            }

        }

       native_setup()是jni 如下:

static void

android_os_UEventObserver_native_setup(JNIEnv *env, jclass clazz)

{

    if (!uevent_init()) {

        jniThrowException(env, "java/lang/RuntimeException",

                          "Unable to open socket for UEventObserver");

    }

}

    uevent_init()是hal,创建用于监听内核uevent事件的socket, 如下:

 

int uevent_init()

{

    struct sockaddr_nl addr;

    int sz = 64*1024;

    int s;

 

    memset(&addr, 0, sizeof(addr));

    addr.nl_family = AF_NETLINK;

    addr.nl_pid = getpid();

    addr.nl_groups = 0xffffffff;

 

    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

    if(s < 0)

        return 0;

 

    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));

 

    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {

        close(s);

        return 0;

    }

 

    fd = s;

    return (fd > 0);

}

 

     next_event是jni,如下:

static int

android_os_UEventObserver_next_event(JNIEnv *env, jclass clazz, jbyteArray jbuffer)

{

    int buf_sz = env->GetArrayLength(jbuffer);

    char *buffer = (char*)env->GetByteArrayElements(jbuffer, NULL);

    int length = uevent_next_event(buffer, buf_sz - 1);

    env->ReleaseByteArrayElements(jbuffer, (jbyte*)buffer, 0);

    return length;

}

    uevent_next_event是hal, 等待uevent事件, 如下:

int uevent_next_event(char* buffer, int buffer_length)

{

    while (1) {

        struct pollfd fds;

        int nr;

   

        fds.fd = fd;

        fds.events = POLLIN;

        fds.revents = 0;

        nr = poll(&fds, 1, -1);

    

        if(nr > 0 && fds.revents == POLLIN) {

            int count = recv(fd, buffer, buffer_length, 0);

            if (count > 0) {

                return count;

            }

        }

    }

   

    // won't get here

    return 0;

}

       至于netlink socket的原理不做过多解释, 我们现在更关心的是我们的电池驱动如何发送出uevent。

2.1.1.2.    uevent

          关于linux设备模型请参考《LDD3》的第14章。

          如果对节点的动态动态创建过程有了解的话, 那么对uevent一定不会陌生。 当发现设备调用device_add函数时, device_add便会调用kobject_uevent函数发出一个uevent。 应用层的udev(当然android是没有udev的, 动态节点创建时由init进程做的)就会监听这个uevent来创建设备节点。 当然当设备移除时也会调用device_remove也会发出一个uevent,udev收到这个消息就删除设备节点。

          每个uevent都包含一些属性, 所有uevent都有如下属性, 下面只列出几个重要的:

l         ACTION: add, remove或change等。

l         SUBSYSTEM:“devices”,“input”,“power_supply”等。

l         DEVPATH:发出uevent的kobject的sys对应路径。

          当然不同的子系统中发出的uevent又会可能有相应的属性发出, 还记得每个kset对应一个子系统吧, 比如/sys/devices, /sys/block, /sys/class/input, /sys/class/power_supply, 当添加一个kset时会为这个kset注册一个kset_uevent_ops。 当一个kobject发出一个uevent时, 就会去寻找这个kobject所属的kset, 然后调用这个kset里面的kset_uevent_ops来添加子系统相关的一些属性。比如power_supply子系统里面发出的uevent都会有POWER_SUPPLY_NAME这个属性, 值可以是“ac”, “usb”或者“battery”。

          当然驱动也是可以为uevent添加属性的, 调用kobject_uevent_env函数发uevent即可,kobject_uevent_env声明如下:

       int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])

       发出去uevent, 应用层就可以通过netlink socket的方式来接收。

2.1.2.          状态读取

       update()函数里会调用nativeUpdate()通过进入c++层, nativeUpdate在com_android_server_BatteryService.cpp中。 Android的Linux 内核中的电池驱动会提供如下sysfs接口给

FrameWork:

/sys/class/power_supply/ac/online
/sys/class/power_supply/ac/type
 
/sys/class/power_supply/usb/online
/sys/class/power_supply/usb/type
 
/sys/class/power_supply/battery/status
/sys/class/power_supply/battery/health
/sys/class/power_supply/battery/present
/sys/class/power_supply/battery/capacity
/sys/class/power_supply/battery/batt_vol
/sys/class/power_supply/battery/batt_temp
/sys/class/power_supply/battery/technology
/sys/class/power_supply/battery/type

 

       当监听到power_supply变化的消息后, nativeUpdate函数就会重新读取以上sysfs文件获得当前状态。这里应该注意的是读到的capacity为0-100之间的数值, 100代表满电。

2.1.3.          更新显示

       当发现有变化时就会发出BroadCast, 关心电池变化事件的模块就会定义BroadCastReceiver来响应, 比如状态栏。有如下intent会在相应情况下通过BroadCast发出:

l         Intent.ACTION_BATTERY_CHANGED:不仅是电量变换, 包括connected和disconnected等所有变化, 都会发出这个broadcast。

l         Intent.ACTION_POWER_CONNECTED

l         Intent.ACTION_POWER_DISCONNECTED

l         Intent.ACTION_BATTERY_LOW

l         Intent.ACTION_BATTERY_OKAY

 

       通过在Eclipse中进行search, 找到关心Intent.ACTION_BATTERY_CHANGED的地方有:

l         NotificationManagerService:更新电源灯的更亮度和让通知灯闪几下。

l         PowerManagerService: 代码如下

    private final class BatteryReceiver extends BroadcastReceiver {

        @Override

        public void onReceive(Context context, Intent intent) {

            synchronized (mLocks) {

                boolean wasPowered = mIsPowered;

                mIsPowered = mBatteryService.isPowered();

 

                if (mIsPowered != wasPowered) {

                    // update mStayOnWhilePluggedIn wake lock

                    updateWakeLockLocked();

 

                    // treat plugging and unplugging the devices as a user activity.

                    // users find it disconcerting when they unplug the device

                    // and it shuts off right away.

                    // to avoid turning on the screen when unplugging, we only trigger

                    // user activity when screen was already on.

                    // temporarily set mUserActivityAllowed to true so this will work

                    // even when the keyguard is on.

                    synchronized (mLocks) {

                        if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) {

                            forceUserActivityLocked();

                        }

                    }

                }

            }

        }

    }

l         UiModeManagerService: 会申请一个FULL_WAKE_LOCK, keep screen on when charging and in car mode。

l         WifiService:注释如下

  

l         com.android.server.Connectivity.Tethering: 代码如下,还不清楚什么用。

if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {

    mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)

                        == BatteryManager.BATTERY_PLUGGED_USB);

    Tethering.this.updateUsbStatus();

}

l         packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

l         KeyguardUpdateMonitor:代码如下, 还不清楚作用。

private void handleBatteryUpdate(int pluggedInStatus,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);

            }

       }

}

 

关心Intent.ACTION_POWER_CONNECTED的地方有:

l         packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

 

关心Intent.ACTION_POWER_DISCONNECTED的地方有:

 

关心Intent.ACTION_BATTERY_LOW的地方有:

l         packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示, 弹出dialog并发出声音提示用户低电量。

 

关心Intent.ACTION_BATTERY_OKAY的地方有:

l         packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

2.2 驱动

关于驱动实现遵循linux kernel power-supply子系统规范即可, 可参考示例代码较多。

              

3.       用电统计

       

       上图是显示Android系统用电统计结果的界面, 这个界面在Settings->About Phone->Battery use里。

用电统计是对Android电源管理系统缺点的弥补, Android电源管理系统的缺点是只有所有程序设计良好电源管理系统才能工作的出色, 一坏就会坏一锅粥。 当你发现你的系统莫名其妙很快没电了时, 你就会可以通过用电统计找到原因, 以便禁掉某些功能或卸载某些程序。

       com.android.server.BatteryStatsService负责统计, 其它模块比如WakeLock和PowerManagerService会向BatteryStatsService喂数据。BatteryStatsService在ActivityManagerService中创建, 如下所示:

mBatteryStatsService = new BatteryStatsService(new File(systemDir, "batterystats.bin").toString());

可见统计的数据保存在batterystats.bin里面。

Settings程序也是通过BatteryStatsService获得统计数据, 然后把统计数据传入BatteryStatsImpl来分析。

这部分细节比较复杂就先点到为止, 看以后是不是有深入的需要。

 

4.       实际问题

当由充电状态转到电池供电时,读到电池电压会有0。2v左右跳降。 当由电池供电转到充电状态时,读到电池电压会有0。2v左右跳升。这就需要驱动或hal层维护两张电压电量对应表。


这篇关于android 2.3 电量管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908215

相关文章

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、