【Android】Android Framework系列---CarPower深度睡眠STR

Android Framework系列—CarPower深度睡眠

之前博客说了CarPower的开机启动流程
这里分析一下,Android CarPower实现深度睡眠的流程。
首先,什么是深度睡眠(Deep Sleep)?

Android进入Deep Sleep后,关闭屏幕、关闭CPU的电源,保持RAM的电源(激活状态)。深度睡眠会进行Suspend-to-RAM挂起到内存(做车载的经常会听到的STR)。当然深度睡眠,还一种是挂起到硬盘(Suspend-to-Disk),不过目前主要用的是STR。所以STR是深度睡眠时的状态,但STR不等于深度睡眠。

DeepSleep或STR,需要内核配合实现。通过这种技术,车载系统可以进入低能耗模式,同时又可以再用户需要时实现快速(非常快,因为从内存中恢复状态)的启动。
感觉这种方式,跟snapshot快启动有点类似。不过是两个概念上的事情了。

下面摘自Android官网对这种状态的介绍。

也称为“挂起到 RAM”(S2R 或 STR)。将 SoC 置于 S3 电源模式,CPU 断电而 RAM 仍通电。

VMCU 应将 SoC 置于“深度睡眠”状态,并断开应用处理器的电源。然后,AAOS 将进入“挂起到 RAM”状态,但不执行任何代码。

整套深度睡眠的实现,涉及的主要模块(这里指单Android系统。当前还有Qnx之类的hypervisor)

  • MCU/VMCU:控制电源,发送电源状态给SOC(Android)
  • Vehicle HAL:一般由硬件供应商实现,与MCU通信(can、以太方式)
  • CarPowerManagerService(CPMS): 实现Android侧的电源控制,应用电源策略。
  • CarPowerManager(CPM): CPMS的客户端,通知电源状态给应用,等待应用回复状态等。

下面是摘自Android官网的深入睡眠流程描述;

只有 VMCU 可以启动深度睡眠。启动深度睡眠后,VMCU 会通过 VHAL 向 CPMS 发送通知。CPMS 通过使用由 CPM 提供的新状态 ID 调用 onStateChanged() 方法,将状态更改为“关闭准备”并向所有观察者(监控 CPMS 的应用和服务)广播此状态转换。

CPM 在应用/服务与 CPMS 之间进行协调。在 CPM 的 onStateChanged() 方法中,同步调用应用/服务的
onStateChanged() 方法。大多数应用和服务需要完成其准备后才能从该调用返回。特权服务可以在为
PRE_SHUTDOWN_PREPARE、SUSPEND_ENTER 和 POST_SUSPEND_ENTER
返回后,异步继续关闭准备。在此情况下,特权服务应在完成准备时对提供的 CompletablePowerStateChangeFuture
对象调用 complete()。请注意,SHUTDOWN_PREPARE 不允许异步准备。在向 VHAL 发送
DEEP_SLEEP_ENTRY 之前,CPMS 会定期向 VHAL 发送推迟关闭的请求。

当所有 CPM 对象完成关闭准备后,CPMS 会向 VHAL 发送 AP_POWER_STATE_REPORT,VHAL 随后通知
VMCU,告知它 AP 已准备好挂起。CPMS 还会调用其挂起方法以挂起内核。

下面的图,是Android官网对于深度睡眠的时序图。这里贴在这里。后面会根据源码,分析并画一个跟代码对应的时序图。
Android官网深入睡眠流程

CPMS深度睡眠代码实现

  • 到这里,我们开始分析CPMS的实现。代码基于Android11

首先,VMCU请求挂起。Vehicle HAL (Hal层的nativeservice)发送“AP_POWER_STATE_REQ(SHUTDOWN_PREPARE)”, 这样CarService中的 VehicleHal(Client端 )就收到了这个通知。一路通知给CPMS进行状态处理。

  • packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {// 省略for (HalServiceBase s : mServicesToDispatch) {//  通知给监听者s.onHalEvents(s.getDispatchList());s.getDispatchList().clear();}mServicesToDispatch.clear();
}
  • packages/services/Car/service/src/com/android/car/hal/PowerHalService.java
@Override
public void onHalEvents(List<VehiclePropValue> values) {// 省略dispatchEvents(values, listener);
}private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) {for (VehiclePropValue v : values) {switch (v.prop) {case AP_POWER_STATE_REPORT:// Ignore this property event. It was generated inside of CarService.break;case AP_POWER_STATE_REQ:int state = v.value.int32Values.get(VehicleApPowerStateReqIndex.STATE);int param = v.value.int32Values.get(VehicleApPowerStateReqIndex.ADDITIONAL);Log.i(CarLog.TAG_POWER, "Received AP_POWER_STATE_REQ="+ powerStateReqName(state) + " param=" + param);// 通知状态给CPMS对象// AP_POWER_STATE_REQ(SHUTDOWN_PREPARE, CAN_SLEEP)listener.onApPowerStateChange(new PowerState(state, param));break;case DISPLAY_BRIGHTNESS:{// 省略}break;}}
}
  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
@Override
public void onApPowerStateChange(PowerState state) {synchronized (mLock) {mPendingPowerStates.addFirst(new CpmsState(state));mLock.notify();}mHandler.handlePowerStateChange();
}private void handlePowerStateChange() {Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);sendMessage(msg);
}@Override
public void handleMessage(Message msg) {CarPowerManagementService service = mService.get();if (service == null) {Slog.i(TAG, "handleMessage null service");return;}switch (msg.what) {case MSG_POWER_STATE_CHANGE:// 开始处理//  service是doHandlePowerStateChange对象service.doHandlePowerStateChange();break;// 省略}
}private void doHandlePowerStateChange() {// 省略了mCurrentState = state;switch (state.mState) {case CpmsState.WAIT_FOR_VHAL:handleWaitForVhal(state);break;case CpmsState.ON:handleOn();break;case CpmsState.SHUTDOWN_PREPARE:// 处理SHUTDOWN_PREPAREhandleShutdownPrepare(state);break;// 省略default:// Illegal state// TODO:  Throw exception?break;}
}

CarPowerManagementService开始处理SHUTDOWN_PREPARE。主要是关闭VR交互、关闭屏幕。通知状态给CPMS的客户端,启动超时等待客户端处理完成(使用****CarPowerStateListenerWithCompletion的客户端)

  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
private void handleShutdownPrepare(CpmsState newState) {setVoiceInteractionDisabled(true);mSystemInterface.setDisplayState(false);// Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.// 通知状态给客户端sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE);mHal.sendShutdownPrepare();doHandlePreprocessing();
}private void doHandlePreprocessing() {int intervalMs;int pollingCount;synchronized (mLock) {intervalMs = mShutdownPollingIntervalMs;pollingCount = (mShutdownPrepareTimeMs / mShutdownPollingIntervalMs) + 1;}if (Build.IS_USERDEBUG || Build.IS_ENG) {// 这里省略了,主要是可以通过属性,修改超时时间。}Slog.i(TAG, "processing before shutdown expected for: "+ mShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);// 启动超时任务,定期检查。那些使用 CarPowerStateListenerWithCompletion//(带有future,告知CPMS自己是否处理完了)的应用端是否处理完毕。// 如果超时还没完成,强制走DeepSleep流程。synchronized (mLock) {mProcessingStartTime = SystemClock.elapsedRealtime();releaseTimerLocked();mTimer = new Timer();mTimerActive = true;mTimer.scheduleAtFixedRate(new ShutdownProcessingTimerTask(pollingCount),0 /*delay*/,intervalMs);}if (mSwitchGuestUserBeforeSleep) {switchToNewGuestIfNecessary();}
}// 超时处理Task
private class ShutdownProcessingTimerTask extends TimerTask {private final int mExpirationCount;private int mCurrentCount;private ShutdownProcessingTimerTask(int expirationCount) {mExpirationCount = expirationCount;mCurrentCount = 0;}@Overridepublic void run() {synchronized (mLock) {if (!mTimerActive) {// Ignore timer expiration since we got cancelledreturn;}mCurrentCount++;if (mCurrentCount > mExpirationCount) {// 超时处理。给Vehicle HAL通知DEEP_SLEEP_ENTRY,告知可以开始进入深入睡眠状态。PowerHandler handler;releaseTimerLocked();handler = mHandler;handler.handleProcessingComplete();} else {// 定期通过PowerServiceHAL向Vehicle HAL发送延时通知。mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);}}}
}

这里假设走正常的处理了,即所有客户端(CarPowerStateListenerWithCompletion)期限内完成了处理。那么CPMS,就会通过PowerserviceHAL告知 VehicleHAL “DEEP_SLEEP_ENTRY”状态。下面是CarPowerStateListenerWithCompletion的一个例子。

mCarPowerManager.setListenerWithCompletion(mCarPowerStateListener);
private final CarPowerStateListenerWithCompletion mCarPowerStateListener =new CarPowerStateListenerWithCompletion() {@Overridepublic void onStateChanged(int state, CompletableFuture<Void> future) {if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) {// 例子,应用端处理完成后,调用future.complete 告知CPMS处理完成if (future != null) {future.complete(null);}}}
};
  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
// future.complete 会触发CPMS这个函数。
@Override
public void finished(ICarPowerStateListener listener) {ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);ICarImpl.assertCallingFromSystemProcessOrSelf();finishedImpl(listener.asBinder());
}private void finishedImpl(IBinder binder) {boolean allAreComplete;synchronized (mLock) {mListenersWeAreWaitingFor.remove(binder);allAreComplete = mListenersWeAreWaitingFor.isEmpty();}// 所有Listener处理完成了。if (allAreComplete) {signalComplete();}
}private void signalComplete() {if (mCurrentState.mState == CpmsState.SHUTDOWN_PREPARE|| mCurrentState.mState == CpmsState.SIMULATE_SLEEP) {PowerHandler powerHandler;// All apps are ready to shutdown/suspend.synchronized (mLock) {if (!mShutdownOnFinish) {if (mLastSleepEntryTime > mProcessingStartTime&& mLastSleepEntryTime < SystemClock.elapsedRealtime()) {Slog.i(TAG, "signalComplete: Already slept!");return;}}powerHandler = mHandler;}Slog.i(TAG, "Apps are finished, call handleProcessingComplete()");powerHandler.handleProcessingComplete();}
}private void handleProcessingComplete() {removeMessages(MSG_PROCESSING_COMPLETE);Message msg = obtainMessage(MSG_PROCESSING_COMPLETE);sendMessage(msg);
}@Override
public void handleMessage(Message msg) {CarPowerManagementService service = mService.get();if (service == null) {Slog.i(TAG, "handleMessage null service");return;}switch (msg.what) {case MSG_POWER_STATE_CHANGE:service.doHandlePowerStateChange();break;case MSG_DISPLAY_BRIGHTNESS_CHANGE:service.doHandleDisplayBrightnessChange(msg.arg1);break;case MSG_MAIN_DISPLAY_STATE_CHANGE:service.doHandleMainDisplayStateChange((Boolean) msg.obj);break;case MSG_PROCESSING_COMPLETE:// 走这里service.doHandleProcessingComplete();break;}
}private void doHandleProcessingComplete() {int listenerState;synchronized (mLock) {releaseTimerLocked();if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {// entered sleep after processing start. So this could be duplicate request.Slog.w(TAG, "Duplicate sleep entry request, ignore");return;}// 可以设置关机,即不进入挂起。// 默认状态,走SUSPEND_ENTERlistenerState = mShutdownOnFinish? CarPowerStateListener.SHUTDOWN_ENTER : CarPowerStateListener.SUSPEND_ENTER;}// 处理状态变化(这个函数是两参的,并且是private的)onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, listenerState);
}private void onApPowerStateChange(int apState, int carPowerStateListenerState) {CpmsState newState = new CpmsState(apState, carPowerStateListenerState);synchronized (mLock) {mPendingPowerStates.addFirst(newState);mLock.notify();}// 用 handler线程处理。下面省略了中间流程,最终调用的是doHandlePowerStateChangemHandler.handlePowerStateChange();
}private void doHandlePowerStateChange() {mCurrentState = state;switch (state.mState) {// 省略case CpmsState.WAIT_FOR_FINISH:// 走这里handleWaitForFinish(state);break;// 省略default:// Illegal state// TODO:  Throw exception?break;}
}private void handleWaitForFinish(CpmsState state) {//  通知给CPMS的监听者当前状态sendPowerManagerEvent(state.mCarPowerStateListenerState);int wakeupSec;synchronized (mLock) {// If we're shutting down immediately, don't schedule// a wakeup time.//  可以设置,进入休眠状态后的唤醒时间(默认是不设置的),0表示一直Sleep(除非VMCU主动唤起)wakeupSec = mGarageModeShouldExitImmediately ? 0 : mNextWakeupSec;}switch (state.mCarPowerStateListenerState) {case CarPowerStateListener.SUSPEND_ENTER:// 	// 给Vehicle  HAL发送状态// 发送的是DEEP_SLEEP_ENTRY,告知可以进入休眠状态了。mHal.sendSleepEntry(wakeupSec);break;case CarPowerStateListener.SHUTDOWN_ENTER:mHal.sendShutdownStart(wakeupSec);break;}
}

到这里,就是告诉了VMCU,Android 挂起Ready了,也就是一切准备完成。然后MCU就会发送AP_POWER_STATE_REQ(FINISHED),告知Android侧调用Linux内核的实现,把自己挂起来(Supend)。消息通知流程,跟上面的onPropertyEvent一致,这部分下面就省略了。

  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
private void doHandlePowerStateChange() {mCurrentState = state;switch (state.mState) {// 省略case CpmsState.SUSPEND:// Received FINISH from VHAL// 走这里handleFinish();break;default:// Illegal state// TODO:  Throw exception?break;}
}private void handleFinish() {// 省略// 关闭VoicesetVoiceInteractionDisabled(true);// To make Kernel implementation simpler when going into sleep.disableWifi();if (mustShutDown) {// shutdown HUmSystemInterface.shutdown();} else {// 处理深入睡眠doHandleDeepSleep(simulatedMode);}mShutdownOnNextSuspend = false;
}private void doHandleDeepSleep(boolean simulatedMode) {// keep holding partial wakelock to prevent entering sleep before enterDeepSleep call// enterDeepSleep should force sleep entry even if wake lock is kept.mSystemInterface.switchToPartialWakeLock();mHandler.cancelProcessingComplete();synchronized (mLock) {mLastSleepEntryTime = SystemClock.elapsedRealtime();}int nextListenerState;if (simulatedMode) {// 省略} else {// 最终会调用Kernel接口,Supend To RAM(感兴趣的小伙伴可以顺着这个接口看下去。// 挂成功的情况下面,下面的代码就不会走了(CPU都没电了)。// 之后的代码,就是CPU上电后的恢复流程了。// 可以看出恢复前,要刷新一下屏幕亮度。然后走WAIT_FOR_VHAL状态。boolean sleepSucceeded = suspendWithRetries();if (!sleepSucceeded) {// Suspend failed and we shut down instead.// We either won't get here at all or we will power off very soon.return;}// We suspended and have now resumednextListenerState = CarPowerStateListener.SUSPEND_EXIT;}synchronized (mLock) {mIsResuming = true;// Any wakeup time from before is no longer valid.mNextWakeupSec = 0;}Slog.i(TAG, "Resuming after suspending");mSystemInterface.refreshDisplayBrightness();onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState);
}

综上是Android CarPower的深入睡眠睡眠主要流程,实际开发过程中。根据业务进行扩展,一般会添加一个跟CPMS平行,或者基于它的新的电源模块。下面是上面代码的时序图,供参考。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内某发动机制造工厂RFID智能制造应用解决方案

一、工厂布局和装备 国内某发动机制造工厂的装配车间布局合理&#xff0c;设备先进&#xff0c;在这个5万平方米的生产区域内&#xff0c;各个工位之间流程紧密&#xff0c;工厂采用了柔性设备&#xff0c;占比达到了67%&#xff0c;数控化率超过90%&#xff0c;自动化率达到了…

Android java Handler sendMessage使用Parcelable传递实例化对象,我这里传递Bitmap 图片数据

一、Bundle给我们提供了一个putParcelable(key,value)的方法。专门用于传递实例化对象。 二、我这里传递Bitmap 图片数据&#xff0c;实际使用可以成功传统图像数据。 发送&#xff1a;Bundle bundle new Bundle();bundle.putParcelable("bitmap",bitmap);msg.setD…

亚马逊 JDK下载地址

下载地址 https://docs.aws.amazon.com/corretto/选择版本 选择操作系统 比如 windows64 位 可以选择安装包或者解压版本 msi 的为安装版 zip 的为解压版

删除链表的倒数第 N 个结点

LeetCode 19. 删除链表的倒数第 N 个结点 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *n…

【Linux系统化学习】开发工具——gdb(调试器)

个人主页点击直达&#xff1a;小白不是程序媛 Linux专栏&#xff1a;Linux系统化学习 个人仓库&#xff1a;Gitee 目录 前言&#xff1a; gdb版本检查和安装 Debug和Release gdb的使用 其他指令 前言&#xff1a; 前几篇文章分别介绍了在Linux下的代码编辑器、编译器。…

python 深度学习 解决遇到的报错问题9

本篇继python 深度学习 解决遇到的报错问题8-CSDN博客 目录 一、can only concatenate str (not "int") to str 二、cant convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, in…

MATLAB - Gazebo 联合仿真 —— 使用 UR10 机械臂检测和采摘水果

系列文章目录 文章目录 系列文章目录前言一、设置 Gazebo 仿真环境二、在 Gazebo 中模拟和控制机器人2.1 概述2.2 任务调度器2.3 感知和目标生成系统2.4 运动规划2.5 机械臂和关节控制系统 三、分配用于控制机器人的参数3.1 定义机器人模型和运动规划参数&#xff0c;3.2 定义机…

历法、节日、节气

目录 一&#xff0c;阳历、阴历、公历、农历 1&#xff0c;阳历、阴历 2&#xff0c;公历&#xff0c;农历 二&#xff0c;双历合并 1&#xff0c;组成要素 2&#xff0c;一年 3&#xff0c;一月 4&#xff0c;一日 三&#xff0c;闰法则 1&#xff0c;闰秒 2&#…

分布式单元化

一 分布式单元化 1.1 两地三中心 顾名思义&#xff0c;两地指的是两个城市&#xff1a;同城&#xff0c;异地。三中心指的是三个数据中心&#xff1a;生产中心、同城容灾中心、异地容灾中心。 在同一个城市或者临近的城市建设两个相同的系统&#xff0c;双中心具备相当的业…

【漏洞复现】Webmin 远程命令执行(CVE-2019-15107)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞验证 1.5、深度利用1、反弹Shell 1.6、修复建议1.7、参考链接 说明内容漏洞编号CVE-2019-15107漏洞…

如何发布自己的golang库

如何发布自己的golang库 1、在 github/gitee 上创建一个 public 仓库&#xff0c;仓库名与 go 库名一致&#xff0c;然后将该仓库 clone 到本地。 本文这里使用 gitee。 $ git clone https://gitee.com/zsx242030/goutil.git2、进入项目文件夹&#xff0c;进行初始化。 $ go…

数字人IP为何成家电品牌年轻化营销黑马?

伴随着数字人概念的出现&#xff0c;家电品牌逐渐通过3D虚拟数字人定制&#xff0c;让数字人成为内容、变现一体的IP&#xff0c;形成一定影响力的品牌效应&#xff0c;利用长线内容沉淀粉丝&#xff0c;使品牌实现年轻化营销。 *图片源于网络 如近日在海尔智家旗下品牌发布会上…