【Android】电源管理,进入和退出 Suspend To RAM

汽车电源管理

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 和 HAlClientpackages/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
libsuspendsystem/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。

时序图

在这里插入图片描述

参考

google 官方文档

蜗窝科技

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在灵动微mm32f系列的 tinyusb 例程中,进入退出suspend模式的代码如下: 进入suspend模式: ```c void tud_suspend_cb(void) { // Wake up by uart or button press // so no need to do anything here // but must call tud_rh_suspend_complete() to acknowledge the suspend request tud_rh_suspend_complete(); } // Invoked when usb bus is resumed void tud_resume_cb(void) { (void) tud_control_xfer(0, 0, 0, NULL, 0); // dummy control request to wake up host if it is sleeping } ``` 在进入suspend模式时,需要调用tud_rh_suspend_complete()函数,以确认收到suspend请求。当usb bus被唤醒时,调用tud_resume_cb()函数。 退出suspend模式: ```c // Invoked when received control request to enter DFU mode bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * p_request) { switch (p_request->bmRequestType_bit.typeRecipient) { case TUSB_REQ_TYPE_STANDARD: switch (p_request->bRequest) { case TUSB_REQ_SET_FEATURE: if ( p_request->wValue == 0 ) tud_suspend(); break; case TUSB_REQ_CLEAR_FEATURE: if ( p_request->wValue == 0 ) tud_resume(); break; } break; } return false; } void tud_suspend(void) { // Stop usb usb_device_disconnect(); // Enter into deep sleep mode // ... } void tud_resume(void) { // Leave deep sleep mode // ... // Start usb usb_device_connect(); } ``` 在退出suspend模式时,需要在tud_control_request_cb()函数中,根据收到的控制请求来判断是否需要退出suspend模式。在tud_suspend()函数中,停止usb设备,并进入深度睡眠模式。在tud_resume()函数中,退出深度睡眠模式,并重新启动usb设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值