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 函数的值决定是否调用关机的流程.
关机的几个判断条件:
mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL
判断是否处于临界状态,关不了机的原因mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
电量为0且当前没有接入充电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 12月 3 2022 ./ drwxrwxr-x 50 anson anson 4096 12月 3 2022 ../ drwxrwxr-x 11 anson anson 4096 12月 3 2022 1.0/ drwxrwxr-x 10 anson anson 4096 12月 3 2022 2.0/ drwxrwxr-x 9 anson anson 4096 12月 3 2022 2.1/ drwxrwxr-x 3 anson anson 4096 12月 3 2022 storage/ drwxrwxr-x 4 anson anson 4096 12月 3 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