汽车电源管理
CarPowerManagementService 实现 Suspend To RAM 以及 Shutdown;
CarPowerManager 电源状态改变时的回调;
深度睡眠功能(将 Android 挂起到 RAM)在内核中实现。此功能以位于 /sys/power/state 的特殊文件形式提供给用户空间。AAOS 通过将 mem 写入此文件而挂起。
代码路径
内容 | 目录 |
---|---|
与 CarPowerManager 相关的代码 | packages/services/Car/car-lib/src/android/car/hardware/power |
CarPowerManagementService 等等 | packages/services/Car/service/src/com/android/car |
处理 VHAL 的服务,如 VehicleHal 和 HAlClient | packages/services/Car/service/src/com/android/car/hal |
VHAL 接口和属性定义 | hardware/interfaces/automotive/vehicle/2.0 |
介绍 CarPowerManager 的示例应用 | packages/services/Car/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink |
CarServiceHelperService 相关的代码 | frameworks/opt/car/services |
libsuspend | system/core/libsuspend |
进入到 Suspend To RAM
当 VHAL 层调用 doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::SHUTDOWN_PREPARE, (int)VehicleApPowerStateShutdownParam::CAN_SLEEP));
表明收到请求进入 Suspend To RAM。
时序图
VHAL 层的调用
doHalEvent 这个函数实现的功能需要从 VehicleHalManager.cpp
这个文件的 init 函数看起。
void VehicleHalManager::init() {
ALOGI("VehicleHalManager::init");
mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize);
//起一个线程处理EventQueue
mBatchingConsumer.run(&mEventQueue,
kHalEventBatchingTimeWindow,
std::bind(&VehicleHalManager::onBatchHalEvent,
this, _1));
//doHalEvent 实际上就是 &VehicleHalManager::onHalEvent
mHal->init(&mValueObjectPool,
std::bind(&VehicleHalManager::onHalEvent, this, _1),
std::bind(&VehicleHalManager::onHalPropertySetError, this,
_1, _2, _3));
// Initialize index with vehicle configurations received from VehicleHal.
auto supportedPropConfigs = mHal->listProperties();
mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs));
std::vector<int32_t> supportedProperties(
supportedPropConfigs.size());
for (const auto& config : supportedPropConfigs) {
supportedProperties.push_back(config.prop);
}
}
//所以 doHalEvent 做的就是往队列里面添加一个event
void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
mEventQueue.push(std::move(v));
}
void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) {
const auto& clientValues =
mSubscriptionManager.distributeValuesToClients(values, SubscribeFlags::EVENTS_FROM_CAR);
for (const HalClientValues& cv : clientValues) {
auto vecSize = cv.values.size();
hidl_vec<VehiclePropValue> vec;
if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
} else {
vec.resize(vecSize);
}
int i = 0;
for (VehiclePropValue* pValue : cv.values) {
shallowCopy(&(vec)[i++], *pValue);
}
//最后由线程把队列中的事件通过 HIDL 分发给 CPMS
auto status = cv.client->getCallback()->onPropertyEvent(vec);
if (!status.isOk()) {
ALOGE("Failed to notify client %s, err: %s",
toString(cv.client->getCallback()).c_str(),
status.description().c_str());
}
}
}
CPMS 层的调用
通过 HIDL 回调上来的接口在 VehicleHal.java
中, 然后通过 PowerHalService.java
调用到 CarPowerManagementService.java
中。
相关代码如下:
//VehicleHal.java
@Override
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
synchronized (this) {
for (VehiclePropValue v : propValues) {
HalServiceBase service = mPropertyHandlers.get(v.prop);
if(service == null) {
Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x"
+ toHexString(v.prop));
continue;
}
service.getDispatchList().add(v);
mServicesToDispatch.add(service);
VehiclePropertyEventInfo info = mEventLog.get(v.prop);
if (info == null) {
info = new VehiclePropertyEventInfo(v);
mEventLog.put(v.prop, info);
} else {
info.addNewEvent(v);
}
}
}
for (HalServiceBase s : mServicesToDispatch) {
s.handleHalEvents(s.getDispatchList());
s.getDispatchList().clear();
}
mServicesToDispatch.clear();
}
//PowerHalService.java
@Override
public void handleHalEvents(List<VehiclePropValue> values) {
PowerEventListener listener;
synchronized (this) {
if (mListener == null) {
if (mQueuedEvents == null) {
mQueuedEvents = new LinkedList<>();
}
mQueuedEvents.addAll(values);
return;
}
listener = mListener;
}
dispatchEvents(values, listener);
}
private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) {
for (VehiclePropValue v : values) {
switch (v.prop) {
case AP_POWER_STATE_REPORT:
// Should never see this; write-only property
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);
listener.onApPowerStateChange(new PowerState(state, param));
break;
case DISPLAY_BRIGHTNESS:
{
int maxBrightness;
synchronized (this) {
maxBrightness = mMaxDisplayBrightness;
}
int brightness = v.value.int32Values.get(0) * MAX_BRIGHTNESS / maxBrightness;
if (brightness < 0) {
Log.e(CarLog.TAG_POWER, "invalid brightness: " + brightness + ", set to 0");
brightness = 0;
} else if (brightness > MAX_BRIGHTNESS) {
Log.e(CarLog.TAG_POWER, "invalid brightness: " + brightness + ", set to "
+ MAX_BRIGHTNESS);
brightness = MAX_BRIGHTNESS;
}
Log.i(CarLog.TAG_POWER, "Received DISPLAY_BRIGHTNESS=" + brightness);
listener.onDisplayBrightnessChange(brightness);
}
break;
}
}
}
//CarPowerManagementService.java
@Override
public void onApPowerStateChange(PowerState state) {
PowerHandler handler;
synchronized (CarPowerManagementService.this) {
mPendingPowerStates.addFirst(new CpmsState(state));
handler = mHandler;
}
handler.handlePowerStateChange();
}
CPM 层的回调
实现就是通过 AIDL 的回调
//CarPowerManagementService.java
private void notifyListeners(PowerManagerCallbackList listenerList, int newState) {
int idx = listenerList.beginBroadcast();
while (idx-- > 0) {
ICarPowerStateListener listener = listenerList.getBroadcastItem(idx);
try {
listener.onStateChanged(newState);
} catch (RemoteException e) {
// It's likely the connection snapped. Let binder death handle the situation.
Log.e(CarLog.TAG_POWER, "onStateChanged() call failed: " + e, e);
}
}
listenerList.finishBroadcast();
}
CarServiceHelperService 层的调用
在CarPowerManagementService.java
通过 SystemStateInterface.java
的 AIDL 调用 CarServiceHelperService.java
的 forceSuspend 函数;
forceSuspend 函数通过 libsuspend 库进入 Suspend To RAM
//CarPowerManagementService.java
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();
PowerHandler handler;
synchronized (CarPowerManagementService.this) {
handler = mHandler;
}
handler.cancelProcessingComplete();
synchronized (CarPowerManagementService.this) {
mLastSleepEntryTime = SystemClock.elapsedRealtime();
}
int nextListenerState;
if (simulatedMode) {
simulateSleepByLooping();
nextListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
} else {
boolean sleepSucceeded = mSystemInterface.enterDeepSleep();
if (mEnableDeepSleepRetry) {
// Retry to deep sleep if it fails to sleep or system is waken up by alarm timer
Log.i(CarLog.TAG_POWER, "create deep sleep timer, sleepSucceeded: " + sleepSucceeded);
createDeepSleepTimer(sleepSucceeded);
return;
}
if (!sleepSucceeded) {
// VHAL should transition CPMS to shutdown.
Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Now attempting to shut down.");
mSystemInterface.shutdown();
}
nextListenerState = CarPowerStateListener.SUSPEND_EXIT;
}
// On wake, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
mNextWakeupSec = 0;
mSystemInterface.refreshDisplayBrightness();
onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState);
}
//SystemStateInterface.java
@Override
public boolean enterDeepSleep() {
boolean deviceEnteredSleep;
//TODO set wake up time via VHAL, bug: 32061842
try {
int retVal;
retVal = mICarServiceHelper.forceSuspend(SUSPEND_TRY_TIMEOUT_MS);
deviceEnteredSleep = retVal == 0;
} catch (Exception e) {
Log.e(TAG, "Unable to enter deep sleep", e);
deviceEnteredSleep = false;
}
return deviceEnteredSleep;
}
//CarServiceHelperService.java
private class ICarServiceHelperImpl extends ICarServiceHelper.Stub {
/**
* Force device to suspend
*/
@Override // Binder call
public int forceSuspend(int timeoutMs) {
int retVal;
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
retVal = nativeForceSuspend(timeoutMs);
} finally {
Binder.restoreCallingIdentity(ident);
}
return retVal;
}
}
退出 Suspend To RAM
通过 GIS 发送 wakeup 事件到 CPU,从而达到中断的效果。
因为这边是模拟的 Power Key 来中断, 所以当被中端的时候系统会通过 libinputflinger 监听 key 事件,然后会上报这个事件。
Power key 事件会让 PowerManagerService wakeup, 从而会中断 CPMS 尝试重新进入深度睡眠的线程。
进而会导致退出 Suspend To RAM。