Android 11 低电量自动关机失效

news2024/12/28 19:36:34

Android 11 低电量自动关机

在这里插入图片描述

概述

安卓系统设计了低电关机功能,旨在当手机电池电量过低时自动关机,以保护手机硬件和数据安全。该功能由以下几个部分组成:

  • 电池电量监测: 安卓系统通过 BatteryService 组件持续监测电池电量。BatteryService会从底层获取电池电量信息,并根据预设的阈值判断电池电量是否过低。
  • 低电量警告: 当电池电量低于预设的警告阈值时,BatteryService会触发低电量警告。低电量警告通常会以弹窗声音提示的形式通知用户。
  • 低电关机: 当电池电量低于预设的关机阈值时,BatteryService会触发低电关机。低电关机前,系统会提示用户保存数据关闭正在运行的应用

基于RK3568 Android 11 系统开发过程中, 移植了电源和电池相关的驱动后, 测试发现低电自动关机的功能失效了.

分析

首先, 从dumpsys 中看下当前电池的一些状态信息(PS: 新版本的内容呈现有所不同):

  • dumpsys battery

    battery
    Current Battery Service state:
      AC powered: false
      USB powered: false
      Wireless powered: false
      Max charging current: 0
      Max charging voltage: 0
      Charge counter: 0
      status: 3
      health: 2
      present: true
      level: 0
      scale: 100
      voltage: 11
      current: 640
      temperature: 308
      technology: Li-ion
    
  • 处理低电关机的关键代码: frameworks/base/services/core/java/com/android/server/BatteryService.java

        private void processValuesLocked(boolean force) {
            boolean logOutlier = false;
            long dischargeDuration = 0;
    
            mBatteryLevelCritical =
                mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
            if (mHealthInfo.chargerAcOnline) {
                mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
            } else if (mHealthInfo.chargerUsbOnline) {
                mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
            } else if (mHealthInfo.chargerWirelessOnline) {
                mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
            } else {
                mPlugType = BATTERY_PLUGGED_NONE;
            }
    
            if (DEBUG) {
                Slog.d(TAG, "Processing new values: "
                        + "info=" + mHealthInfo
                        + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                        + ", mPlugType=" + mPlugType);
            }
    
            // Let the battery stats keep track of the current level.
            try {
                mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
                        mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
                        mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
                        mHealthInfo.batteryFullCharge,
                        mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
            } catch (RemoteException e) {
                // Should never happen.
            }
    
            shutdownIfNoPowerLocked();
            shutdownIfOverTempLocked();
            //....................
        }    
        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 (shouldShutdownLocked()) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mActivityManagerInternal.isSystemReady()) {
                            Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                            intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                            intent.putExtra(Intent.EXTRA_REASON,
                                    PowerManager.SHUTDOWN_LOW_BATTERY);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                        }
                    }
                });
            }
        }
    
        private boolean shouldShutdownLocked() {
            if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
                if (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL) {
                    Slog.w(TAG, "batteryCapacityLevel is CRITICAL need Shutdown");
                    return true;
                } else if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.LOW) {
                    return false;
                }
            }
            if (mHealthInfo.batteryLevel > 0) {
                return false;
            }
    
            // Battery-less devices should not shutdown.
            if (!mHealthInfo.batteryPresent) {
                return false;
            }
    
            // If battery state is not CHARGING, shutdown.
            // - If battery present and state == unknown, this is an unexpected error state.
            // - If level <= 0 and state == full, this is also an unexpected state
            // - All other states (NOT_CHARGING, DISCHARGING) means it is not charging.
            boolean isNotCharging = mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
            if (isNotCharging) {
                return true;
            }
            boolean isExcessDischarge = mHealthInfo.batteryCurrent < 0;
            if (isExcessDischarge) {
                Slog.w(TAG, "batteryCurrent=" + mHealthInfo.batteryCurrent + ", isExcessDischarge need Shutdown");
            }
            return isExcessDischarge;
        }
    

    shouldShutdownLocked 函数的值决定是否调用关机的流程.

    关机的几个判断条件:

    1. mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL 判断是否处于临界状态, 关不了机的原因
    2. mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING; 电量为0且当前没有接入充电
    3. mHealthInfo.batteryCurrent < 0; 充电接入, 放电大于充电电流.

    小插曲-找不到的源码

    在BatteryService.java中, import了一堆health.Vx_x 的类, 搜遍了framework, device, packages没找到相关的源码.

    import android.hardware.health.V1_0.HealthInfo;
    import android.hardware.health.V2_0.IHealth;
    import android.hardware.health.V2_0.Result;
    import android.hardware.health.V2_1.BatteryCapacityLevel;
    import android.hardware.health.V2_1.Constants;
    import android.hardware.health.V2_1.IHealthInfoCallback;
    

    最终在out目录下找到:

  • ll ./out/soong/.intermediates/hardware/interfaces/health

    total 28
    drwxrwxr-x  7 anson anson 4096 123  2022 ./
    drwxrwxr-x 50 anson anson 4096 123  2022 ../
    drwxrwxr-x 11 anson anson 4096 123  2022 1.0/
    drwxrwxr-x 10 anson anson 4096 123  2022 2.0/
    drwxrwxr-x  9 anson anson 4096 123  2022 2.1/
    drwxrwxr-x  3 anson anson 4096 123  2022 storage/
    drwxrwxr-x  4 anson anson 4096 123  2022 utils/
    
  • ./out/soong/.intermediates/hardware/interfaces/health/2.1/android.hardware.health-V2.1-java_gen_java/gen/srcs/android/hardware/health/V2_1/BatteryCapacityLevel.java

    package android.hardware.health.V2_1;
    
    public final class BatteryCapacityLevel {
        /**
         * Battery capacity level is unsupported.
         * Battery capacity level must be set to this value if and only if the
         * implementation is unsupported.
         */
        public static final int UNSUPPORTED = -1 /* -1 */;
        /**
         * Battery capacity level is unknown.
         * Battery capacity level must be set to this value if and only if battery
         * is not present or the battery capacity level is unknown/uninitialized.
         */
        public static final int UNKNOWN = 0 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.UNSUPPORTED implicitly + 1 */;
        /**
         * Battery is at critical level. The Android framework must schedule a
         * shutdown when it sees this value from the HAL.
         */
        public static final int CRITICAL = 1 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.UNKNOWN implicitly + 1 */;
        /**
         * Battery is low. The Android framework may limit the performance of
         * the device when it sees this value from the HAL.
         */
        public static final int LOW = 2 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.CRITICAL implicitly + 1 */;
        /**
         * Battery level is normal.
         */
        public static final int NORMAL = 3 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.LOW implicitly + 1 */;
        /**
         * Battery level is high.
         */
        public static final int HIGH = 4 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.NORMAL implicitly + 1 */;
        /**
         * Battery is full. It must be set to FULL if and only if battery level is
         * 100.
         */
        public static final int FULL = 5 /* ::android::hardware::health::V2_1::BatteryCapacityLevel.HIGH implicitly + 1 */;
        public static final String toString(int o) {
            if (o == UNSUPPORTED) {
                return "UNSUPPORTED";
            }
            if (o == UNKNOWN) {
                return "UNKNOWN";
            }
            if (o == CRITICAL) {
                return "CRITICAL";
            }
            if (o == LOW) {
                return "LOW";
            }
            if (o == NORMAL) {
                return "NORMAL";
            }
            if (o == HIGH) {
                return "HIGH";
            }
            if (o == FULL) {
                return "FULL";
            }
            return "0x" + Integer.toHexString(o);
        }
    
        public static final String dumpBitfield(int o) {
            java.util.ArrayList<String> list = new java.util.ArrayList<>();
            int flipped = 0;
            if ((o & UNSUPPORTED) == UNSUPPORTED) {
                list.add("UNSUPPORTED");
                flipped |= UNSUPPORTED;
            }
            list.add("UNKNOWN"); // UNKNOWN == 0
            if ((o & CRITICAL) == CRITICAL) {
                list.add("CRITICAL");
                flipped |= CRITICAL;
            }
            if ((o & LOW) == LOW) {
                list.add("LOW");
                flipped |= LOW;
            }
            if ((o & NORMAL) == NORMAL) {
                list.add("NORMAL");
                flipped |= NORMAL;
            }
            if ((o & HIGH) == HIGH) {
                list.add("HIGH");
                flipped |= HIGH;
            }
            if ((o & FULL) == FULL) {
                list.add("FULL");
                flipped |= FULL;
            }
            if (o != flipped) {
                list.add("0x" + Integer.toHexString(o & (~flipped)));
            }
            return String.join(" | ", list);
        }
    
    };
    
    
  • ./out/soong/.intermediates/hardware/interfaces/health/1.0/android.hardware.health-V1.0-java-constants_gen_java/gen/android/hardware/health/V1_0/Constants.java

    
    // This file is autogenerated by hidl-gen. Do not edit manually.
    // Source: android.hardware.health@1.0
    // Location: hardware/interfaces/health/1.0/
    
    package android.hardware.health.V1_0;
    
    public class Constants {
        // Values declared in BatteryStatus follow.
        public static final int BATTERY_STATUS_UNKNOWN = 1;
        public static final int BATTERY_STATUS_CHARGING = 2;
        public static final int BATTERY_STATUS_DISCHARGING = 3;
        public static final int BATTERY_STATUS_NOT_CHARGING = 4;
        public static final int BATTERY_STATUS_FULL = 5;
    
        // Values declared in BatteryHealth follow.
        public static final int BATTERY_HEALTH_UNKNOWN = 1;
        public static final int BATTERY_HEALTH_GOOD = 2;
        public static final int BATTERY_HEALTH_OVERHEAT = 3;
        public static final int BATTERY_HEALTH_DEAD = 4;
        public static final int BATTERY_HEALTH_OVER_VOLTAGE = 5;
        public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6;
        public static final int BATTERY_HEALTH_COLD = 7;
    
    }
    
    

    对应的源码目录:

    tree hardware/interfaces/health/
    hardware/interfaces/health/
    ├── 1.0
    │   ├── Android.bp
    │   ├── default
    │   │   ├── Android.bp
    │   │   ├── android.hardware.health@1.0-service.rc
    │   │   ├── convert.cpp
    │   │   ├── Health.cpp
    │   │   ├── Health.h
    │   │   ├── HealthService.cpp
    │   │   ├── include
    │   │   │   └── hal_conversion.h
    │   │   ├── libhealthd
    │   │   │   ├── Android.bp
    │   │   │   └── healthd_board_default.cpp
    │   │   └── README.md
    │   ├── IHealth.hal
    │   ├── types.hal
    │   └── vts
    │       └── functional
    │           ├── Android.bp
    │           └── VtsHalHealthV1_0TargetTest.cpp
    ├── 2.0
    │   ├── Android.bp
    │   ├── default
    │   │   ├── Android.bp
    │   │   ├── Health.cpp
    │   │   ├── healthd_common_adapter.cpp
    │   │   ├── HealthImplDefault.cpp
    │   │   └── include
    │   │       └── health2
    │   │           └── Health.h
    │   ├── IHealth.hal
    │   ├── IHealthInfoCallback.hal
    │   ├── README -> README.md
    │   ├── README.md
    │   ├── types.hal
    │   ├── utils
    │   │   ├── libhealthhalutils
    │   │   │   ├── Android.bp
    │   │   │   ├── HealthHalUtils.cpp
    │   │   │   └── include
    │   │   │       └── healthhalutils
    │   │   │           └── HealthHalUtils.h
    │   │   ├── libhealthservice
    │   │   │   ├── Android.bp
    │   │   │   ├── HealthServiceCommon.cpp
    │   │   │   └── include
    │   │   │       └── health2
    │   │   │           └── service.h
    │   │   ├── libhealthstoragedefault
    │   │   │   ├── Android.bp
    │   │   │   ├── include
    │   │   │   │   └── StorageHealthDefault.h
    │   │   │   └── StorageHealthDefault.cpp
    │   │   └── README.md
    │   └── vts
    │       ├── functional
    │       │   ├── Android.bp
    │       │   └── VtsHalHealthV2_0TargetTest.cpp
    │       └── OWNERS
    ├── 2.1
    │   ├── Android.bp
    │   ├── default
    │   │   ├── Android.bp
    │   │   ├── android.hardware.health@2.1-service.rc
    │   │   ├── android.hardware.health@2.1.xml
    │   │   ├── impl.cpp
    │   │   └── service.cpp
    │   ├── IHealth.hal
    │   ├── IHealthInfoCallback.hal
    │   ├── README.md
    │   ├── types.hal
    │   └── vts
    │       ├── functional
    │       │   ├── Android.bp
    │       │   └── VtsHalHealthV2_1TargetTest.cpp
    │       └── OWNERS
    ├── storage
    │   └── 1.0
    │       ├── Android.bp
    │       ├── default
    │       │   ├── Android.bp
    │       │   ├── android.hardware.health.storage@1.0-service.rc
    │       │   ├── manifest_android.hardware.health.storage@1.0.xml
    │       │   ├── service.cpp
    │       │   ├── Storage.cpp
    │       │   └── Storage.h
    │       ├── IGarbageCollectCallback.hal
    │       ├── IStorage.hal
    │       ├── types.hal
    │       └── vts
    │           └── functional
    │               ├── Android.bp
    │               ├── VtsHalHealthStorageV1_0TargetTest.config
    │               └── VtsHalHealthStorageV1_0TargetTest.cpp
    └── utils
        ├── libhealth2impl
        │   ├── Android.bp
        │   ├── BinderHealth.cpp
        │   ├── HalHealthLoop.cpp
        │   ├── Health.cpp
        │   └── include
        │       └── health2impl
        │           ├── BinderHealth.h
        │           ├── Callback.h
        │           ├── HalHealthLoop.h
        │           └── Health.h
        └── libhealthloop
            ├── Android.bp
            ├── HealthLoop.cpp
            ├── include
            │   └── health
            │       ├── HealthLoop.h
            │       └── utils.h
            └── utils.cpp
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1804069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

展厅设计中的不同区域划分

1、公共区域 公共区域一般来说是不受限制的区域&#xff0c;这种情况下&#xff0c;会使我们想到的区域是大厅、售卖区、视频播放等&#xff0c;这些公共区域的相关设施比较完善&#xff0c;只是需要普通的安全保护设施及警报设备即可。 2、展览区域 展览区域是参观者能够触及到…

创新指南|2024企业如何开启生成式AI创新?从5大应用场景和6步抓手

想要了解如何采用生成式AI来提高企业效率和竞争力&#xff1f;本指南将介绍如何采用生成式AI来实现数字化转型&#xff0c;并打造智能化商业模式。从5大应用场景和6大步骤切入&#xff0c;让您了解如何开启生成式AI创新。立即连线创新专家咨询或观看创新战略方案视频进一步了解…

业务扩张阶段

和之前相比就是服务器的数量增多了 业务系统增多了 每个业务的用户也在增多 采购费用和电费挺多 选课系统一年只用几次&#xff0c;平时不用太浪费服务器的资源&#xff0c;那么怎么才能提高服务器资源的利用率呢 在一个服务器上部署多个不同的业务系统能行吗 不太行&…

基于大模型 Gemma-7B 和 llama_index,轻松实现 NL2SQL

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

生产问题排查:springboot项目启动时注册nacos失败或运行时从nacos闪退

文章目录 一、引出问题二、解决方案1、使用actuator健康检查2、项目启动时判断nacos是否正常连接3、k8s设置探针 一、引出问题 生产项目是用k8s部署的&#xff0c;最近经常遇到启动时注册不到nacos&#xff08;查找nacos的host地址找不到&#xff09;&#xff0c;或者运行的好…

Unity 之 代码修改材质球贴图

Unity 之 代码修改材质球贴图 代码修改Shader&#xff1a;ShaderGraph&#xff1a;材质球包含属性 代码修改 meshRenderer.material.SetTexture("_Emission", texture);Shader&#xff1a; ShaderGraph&#xff1a; 材质球包含属性 materials[k].HasProperty("…

NXP i.MX8系列平台开发讲解 - 3.14 Linux 之Power Supply子系统(一)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. Power Supply子系统介绍 2. Power Supply子系统框架 3. Power Supply代码分析 本章节主要介绍Linux 下的P…

快速入门Linux及使用VSCode远程连接Linux服务器

在当前的技术环境中&#xff0c;Linux操作系统因其强大的功能和灵活性而广受欢迎。无论你是开发人员、系统管理员还是技术爱好者&#xff0c;学习Linux都是提升技术技能的重要一步。本文将介绍如何快速入门Linux&#xff0c;并使用Visual Studio Code&#xff08;VSCode&#x…

【MySQL数据库】:MySQL索引特性

目录 索引的概念 磁盘 磁盘的基本特征 MySQL与磁盘交互的基本单位 索引的理解 建立测试表 理解单个Page 理解多个Page 页目录 单页情况 多页情况 索引的数据结构 聚簇索引 VS 非聚簇索引 索引操作 创建主键索引 创建唯一索引 创建普通索引 创建全文索引 查询…

跨区域文件管控过程中 如何保障安全和效率?

跨区域文件管控是指在跨越不同地域或区域的情况下对文件进行管理和控制的过程。这种控制可能涉及多个方面&#xff0c;包括安全性、合规性和管理效率等。 为了有效进行跨区域文件管控&#xff0c;组织通常需要采取一系列策略和措施&#xff0c;例如&#xff1a; 1、加密和安全…

LeakSearch:针对网络公开凭证的安全扫描与检测工具

关于LeakSearch 在红队演戏过程中&#xff0c;往往需要获取到针对目标域的访问权限。在这个过程中&#xff0c;很多红队人员会选择使用暴露在互联网上的代理服务器来实现目标域的访问&#xff0c;那么此时就需要在互联网上收集公开暴露的凭证信息。 对于蓝队来说&#xff0c;…

项目bug1

大项目测bug的时候让输入数字&#xff0c;如果不是则捕获异常&#xff0c;提示错误&#xff0c;几段很简单的代码&#xff1a; System.out.println("请输入要存入的金额"); Scanner sc new Scanner(System.in); while(true) {try {money sc.nextInt();break;} cat…

Git从入门到放弃

由于我的Git学的不太好&#xff0c;所以为了能够将以后我的学习笔记能够整理的更好&#xff0c;我先要系统的学习一下git&#xff0c;文章由此产生。 文章笔记源自尚硅谷Git入门到精通全套教程视频内容 1 进入官网 学习新技术的第一步需要熟悉官网&#xff0c;Git也不例外。ht…

Spring 使用SSE(Server-Sent Events)学习

什么是SSE SSE 即服务器发送事件&#xff08;Server-Sent Events&#xff09;&#xff0c;是一种服务器推送技术&#xff0c;允许服务器在客户端建立连接后&#xff0c;主动向客户端推送数据。 SSE 基于 HTTP 协议&#xff0c;使用简单&#xff0c;具有轻量级、实时性和断线重…

linux中dd命令以及如何测试读写速度

dd命令详解 dd命令是一个在Unix和类Unix系统中非常常用的命令行工具&#xff0c;它主要用于复制文件和转换文件数据。下面我会详细介绍一些dd命令的常见用法和功能&#xff1a; 基本语法 dd命令的基本语法如下&#xff1a; bash Copy Code dd [option]...主要选项和参数 if…

JS-Fetch

Fetch 是一种用于进行网络请求的现代 JavaScript API。它提供了一种简单、灵活且功能强大的方式&#xff0c;用于从服务器获取资源并处理响应。 Fetch API 在浏览器中原生支持&#xff0c;并且以 Promise 为基础&#xff0c;使得异步请求更加直观和易用。使用 Fetch API&#…

大学搜题软件网课?推荐五个搜题软件和学习工具 #其他#经验分享#知识分享

大学生活中&#xff0c;选择适合自己的学习工具能够提高学习效率&#xff0c;让学习更加轻松愉快。 1.彩虹搜题 这个是公众号 提供了各大教材以及网课平台的练习题答案&#xff0c;强大的平台支持&#xff0c;无论是智慧树还是MOOC&#xff0c;只有老师们用不到&#xff0c;…

【SQLAlChemy】如何定义ORM模型,如何映射到数据库?

定义ORM模型并映射到数据库 创建 ORM 基类 使用 declarative_base 根据 engine 来创建一个 ORM 基类。 from SqlAIchemy.LinkDB.main import engineBase declarative_base()创建自定义类 用上边定义的 Base 类来实现自己的 ORM 类。 __tablename__ 类属性&#xff0c;可以…

Vue 2 + Element UI 选择一个el-select清空另一个el-select选中的值

需求&#xff1a;表单中有两个下拉选择器&#xff0c;先选中第一个&#xff0c;清空第二个选中的值 尝试过this.$refs[form].resetFields(field name);全都失效&#xff01; 效果图如下&#xff1a; 先选择商品分类&#xff0c;再去选择商品列表中的某一件商品 <el-form-…

GDPU JavaWeb Ajax请求

异步请求可以提升用户体验并优化页面性能。 ajax登录 实现ajax异步登录。 注意&#xff0c;ajax用到了jQuery库&#xff0c;先下载好相应的js库&#xff0c;然后复制导入到工程的web目录下&#xff0c;最好与你的前端页面同一层级。然后编写时路径一定要找准&#xff0c;“pag…