Android 11 低电量自动关机
概述
安卓系统设计了低电关机功能,旨在当手机电池电量过低时自动关机,以保护手机硬件和数据安全。该功能由以下几个部分组成:
- 电池电量监测: 安卓系统通过 BatteryService 组件持续监测电池电量。BatteryService会从底层获取电池电量信息,并根据预设的阈值判断电池电量是否过低。
- 低电量警告: 当电池电量低于预设的警告阈值时,BatteryService会触发低电量警告。低电量警告通常会以弹窗或声音提示的形式通知用户。
- 低电关机: 当电池电量低于预设的关机阈值时,BatteryService会触发低电关机。低电关机前,系统会提示用户保存数据并关闭正在运行的应用。
基于RK3568 Android 11 系统开发过程中, 移植了电源和电池相关的驱动后, 测试发现低电自动关机的功能失效了.
分析
首先, 从dumpsys 中看下当前电池的一些状态信息(PS: 新版本的内容呈现有所不同):
-
dumpsys battery
battery Current Battery Service state:AC powered: falseUSB powered: falseWireless powered: falseMax charging current: 0Max charging voltage: 0Charge counter: 0status: 3health: 2present: truelevel: 0scale: 100voltage: 11current: 640temperature: 308technology: 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() {@Overridepublic 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 == 0if ((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