• 【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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 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;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 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_PREPARE
    			handleShutdownPrepare(state);
    			break;
    		// 省略
    		default:
    			// Illegal state
    			// TODO:  Throw exception?
    			break;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    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;
    	}
    
    	@Override
    	public void run() {
    		synchronized (mLock) {
    			if (!mTimerActive) {
    				// Ignore timer expiration since we got cancelled
    				return;
    			}
    			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);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

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

    mCarPowerManager.setListenerWithCompletion(mCarPowerStateListener);
    private final CarPowerStateListenerWithCompletion mCarPowerStateListener =
    		new CarPowerStateListenerWithCompletion() {
    	@Override
    	public void onStateChanged(int state, CompletableFuture<Void> future) {
    		if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) {
    			// 例子,应用端处理完成后,调用future.complete 告知CPMS处理完成
    			if (future != null) {
    				future.complete(null);
    			}
    		}
    	}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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_ENTER
    		listenerState = 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线程处理。下面省略了中间流程,最终调用的是doHandlePowerStateChange
    	mHandler.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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137

    到这里,就是告诉了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() {
    	// 省略
    	// 关闭Voice
    	setVoiceInteractionDisabled(true);
    
    	// To make Kernel implementation simpler when going into sleep.
    	disableWifi();
    
    	if (mustShutDown) {
    		// shutdown HU
    		mSystemInterface.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 resumed
    		nextListenerState = 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

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

  • 相关阅读:
    C++ 17新特性
    图论基础学习笔记
    Python高级语法-装饰器(Python语法糖)
    SVN 修改URL路径-使用重新定位(relocate)命令和找不到问题解决
    vue页面缓存解决方案
    发现一不错的编程助手 Amazon CodeWhisperer
    3D 毛玻璃晶质见证卡
    【matlab程序】海图单位的度分格式
    Python从零基础入门到精通:一个月就够了
    效率倍升:逐浪HMS主题大师1.2-全渠道自动打包、快速传送、锁屏提取、资源审计,移动主题设计辅助利器(全面支持华V米O)
  • 原文地址:https://blog.csdn.net/zxc024000/article/details/134228216