Android AutoMotive—CarPowerManagementService
1、电源状态概述
电源管理是Android AutoMotive OS中较手机Android系统差异化比较大的模块,这是因为车机面临的使用场景比手机更加复杂。既要考虑车辆短时间使用间隔的休眠,也要考虑长时间停放下的关机;休眠状态下可以快速响应唤醒,但是这种状态如果管理不好,又会存在持续耗电的情况,如果电瓶亏电,后果就很严重;同时我们还要考虑到车机系统的OTA升级,通常是在人离开车后,车辆空闲的状态去做的升级,称之为地库模式;还有哨兵模式,在车辆停放的时候摄像头会开启;以及远程控车,通过TBox唤醒车机系统,等等。
对于Android AutoMotive OS,电源状态的迁移如下:
(1) 车机系统处于STR(Suspend To RAM)深度睡眠状态,此时CUP已经掉电,但是RAM内存还保持着原有的数据;
(2) 用户操作车辆,唤醒车机系统,VHAL(Vehicle HAL,车辆硬件抽象层)开始启动车辆相关服务;
(3) 车机系统启动完成;
(4) 用户停止使用车辆,触发关机前的准备流程,等待上层APP做关机前的确认;
(5) 上层APP确认完毕可以关机,并通知到VHAL,等待VHAL关闭;
(6) VHAL关闭完成,AP掉电,车机再次进入STR休眠状态。
2、CarPowerManager
CarPowerManagementService运行在Carservice进程中,它对上层APP提供的客户端API是CarPowerManager,它主要提供了如下几个方法:
方法名 | 注解 |
---|---|
requestShutdownOnNextSuspend() | 请求在下次进入关机流程时直接关机,而非休眠挂起 |
scheduleNextWakeupTime(int seconds) | 设置下次唤醒的时间间隔 |
getPowerState() | 获取到电源状态 |
setListener(@NonNull @CallbackExecutor Executor executor, @NonNull CarPowerStateListener listener) | 设置电源变化监听 |
setListenerWithCompletion(@NonNull @CallbackExecutor Executor executor, @NonNull CarPowerStateListenerWithCompletion listener) | 设置电源变化监听,回调包含结果通知 |
CarPowerStateListenerWithCompletion监听器和CarPowerStateListener监听器不同的是,后者只有一个状态结果的通知;而前者的状态回调接口中,还包含了一个CompletableFuture对象,APP需要通过这个对象告诉CarPowerManagementService,APP层已经完成了关机前的动作,现在可以继续接下来的流程。
电源状态列表如下:
常量定义 | 注解 |
---|---|
STATE_SHUTDOWN_ENTER | 关机 |
STATE_WAIT_FOR_VHAL | 等待VHAL启动 |
STATE_ON | 开机 |
STATE_SHUTDOWN_PREPARE | 进入关机前的准备 |
STATE_SHUTDOWN_CANCELLED | 取消关机 |
STATE_SUSPEND_ENTER | 进入深度休眠,内存供电 |
STATE_HIBERNATION_ENTER | 更深度休眠,内存掉电 |
STATE_SUSPEND_EXIT | 退出休眠 |
需要注意的是,调用setListenerWithCompletion方法除了需要android.car.permission.CAR_POWER权限外,还需要以system UID的用户组运行。这是因为CarPowerStateListenerWithCompletion监听的回调方法,是可以阻塞关机流程的,这必定是要核心的系统级应用和服务才能有权使用。
在手机Android系统中,我们可能会监听BOOT_COMPLETED开机广播,去做程序数据的初始化,但是在AAOS中,如果车机只是休眠唤醒且没有用户切换的情况下,是没有开机广播的。所以除了开机广播外,我们更应该监听电源状态,STATE_ON、STATE_SHUTDOWN_PREPARE是最为常用的,AAOS中特有的地库模式,就是基于对STATE_SHUTDOWN_PREPARE的监听实现的。
3、CarPowerManagementService
CarPowerManagementService的处理流程如下:
VMCU是车辆微控制器,电源的状态上报就是从它发出来的,通过厂商定制的VHAL层到达CarPowermanagementService,接下来就会处理不同的状态,以SHUTDOWN_PREPARE流程为例,代码如下:
// packages/services/Car/service/src/com/android/car/power/CarPowerManagementService.java
private void doHandlePowerStateChange() {
switch (newState.mState) {
case CpmsState.SHUTDOWN_PREPARE:
handleShutdownPrepare(newState, prevState);
}
}
然后调用到doShutdownPrepare()方法,这里会去给各个APP注册的监听器发送状态改变的回调:
private void doShutdownPrepare() {
sendPowerManagerEvent(CarPowerManager.STATE_SHUTDOWN_PREPARE, timeoutMs);
mHal.sendShutdownPrepare();
waitForShutdownPrepareListenersToComplete(timeoutMs, intervalMs);
}
最后会执行到waitForCompletionAsync 方法:
// Waits for listeners to complete.
// If {@code intervalMs} is non-positive value, it is ignored and the method waits up to
// {@code timeoutMs}.
private void waitForCompletionAsync(Runnable taskAtCompletion, Runnable taskAtInterval,
long timeoutMs, long intervalMs) {
//这里使用信号量去进程间通信,获取远程APP prepare的状态。
boolean isNotified = mListenerCompletionSem.tryAcquire(waitTimeMs, TimeUnit.MILLISECONDS);
//超过最大等待时间,则进入下一步流程
if (isLastWait) {
Slogf.w(TAG, "Waiting for listener completion is timeout(%d)",waitTimeMs);
taskAtCompletion.run();
return;
}
//判断所有的监听等待对象是否已经完成准备,等待队列为空之后,也会进入下一步流程
isComplete = mListenersWeAreWaitingFor.isEmpty();
if (isComplete) {
Slogf.i(TAG, "All listeners completed");
taskAtCompletion.run();
mIsListenerWaitingCancelled.set(false);
return;
}
}
所以CarPowermanagementService的执行逻辑总体概括,就是把状态信号回调给各个监听器,同时开启对各个监听器回执状态的检测。
4、Garage Mode 车库模式
车库模式是AAOS中特有的一种空闲模式,它的触发时机是在用户停止使用车辆之后,即收到SHUTDOWN_PREPARE状态信号。车库模式的作用,是利用这段空闲时间,做一些开机时候不能做的事情,最典型的就是系统升级。
Garage Mode对应的服务GarageModeService也是运行在Carservice进程里面,属于CarService的一部分,它内部有一个GarageModeController,会去注册监听CarPowerManagementService的状态:
// packages/services/Car/service/src/com/android/car/garagemode/GarageModeController.java
public class GarageModeController extends ICarPowerStateListener.Stub {
mCarPowerService.registerInternalListener(GarageModeController.this);
@Override
public void onStateChanged(int state, long expirationTimeMs) {
case CarPowerManager.STATE_SHUTDOWN_PREPARE:
initiateGarageMode(
() -> mCarPowerService.completeHandlingPowerStateChange(state,
GarageModeController.this));
break;
}
}
调用registerInternalListener去注册,就会让CarPowerService去把这个监听器加入到上面提到的等待队列中去。
在收到STATE_SHUTDOWN_PREPARE状态后,就会开启车库模式。
// packages/services/Car/service/src/com/android/car/garagemode/GarageModeController.java
void enterGarageMode(Runnable completor) {
broadcastSignalToJobScheduler(true);
startMonitoringThread();
}
这里最重要的就是做了两个事情:一是发送开启车库模式的广播,从方法名可以看到,在广播接收方会去执行JobScheduler任务,也就是OTA升级任务;二是开启对线程的观察,其实也就是观察JobScheduler的JobInfo任务队列是否已经全部执行结束。
跟随广播的发送,我们来到广播的接受方,代码位于frameworks/base/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
CarIdlenessTracker已经不在Carservice的一部分,属于AOSP的framework下,它是由IdleController创建。当设备运行在车载环境时,就会创建CarIdlenessTracker,而在手机上则会创建DeviceIdlenessTracker。这样,CarService就会和原有的Android JobScheduler机制完美结合了起来。
看CarIdlenessTracker:
// frameworks/base/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
public final class CarIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
@Override
public void onReceive(Context context, Intent intent) {
if (action.equals(ACTION_GARAGE_MODE_ON)) {
logIfDebug("GarageMode is on...");
mGarageModeOn = true;
updateIdlenessState();
}
}
private void updateIdlenessState() {
final boolean newState = (mForced || mGarageModeOn);
mIdle = newState;
mIdleListener.reportNewIdleState(mIdle);
}
}
这里就会通知IdleController现在的处于空闲状态,可以去执行相关的JobInfo调度。