| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "chre_api_test_manager.h" |
| |
| #include "chre.h" |
| #include "chre/util/nanoapp/log.h" |
| #include "chre/util/time.h" |
| |
| namespace { |
| constexpr uint64_t kSyncFunctionTimeout = 2 * chre::kOneSecondInNanoseconds; |
| |
| /** |
| * The following constants are defined in chre_api_test.options. |
| */ |
| constexpr uint32_t kThreeAxisDataReadingsMaxCount = 10; |
| |
| /** |
| * Closes the writer and invalidates the writer. |
| * |
| * @param T the RPC message type. |
| * @param writer the RPC ServerWriter. |
| */ |
| template <typename T> |
| void finishAndCloseWriter( |
| Optional<ChreApiTestService::ServerWriter<T>> &writer) { |
| CHRE_ASSERT(writer.has_value()); |
| |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer->Finish(); |
| writer.reset(); |
| } |
| |
| /** |
| * Writes a message to the writer, then closes the writer and invalidates the |
| * writer. |
| * |
| * @param T the RPC message type. |
| * @param writer the RPC ServerWriter. |
| * @param message the message to write. |
| */ |
| template <typename T> |
| void sendFinishAndCloseWriter( |
| Optional<ChreApiTestService::ServerWriter<T>> &writer, const T &message) { |
| CHRE_ASSERT(writer.has_value()); |
| |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| pw::Status status = writer->Write(message); |
| CHRE_ASSERT(status.ok()); |
| finishAndCloseWriter(writer); |
| } |
| |
| /** |
| * Sends a failure message. If there is not a valid writer, this returns |
| * without doing anything. |
| * |
| * @param T the RPC message type. |
| * @param writer the RPC ServerWriter. |
| */ |
| template <typename T> |
| void sendFailureAndFinishCloseWriter( |
| Optional<ChreApiTestService::ServerWriter<T>> &writer) { |
| CHRE_ASSERT(writer.has_value()); |
| |
| T message; |
| message.status = false; |
| sendFinishAndCloseWriter(writer, message); |
| } |
| } // namespace |
| |
| // Start ChreApiTestService RPC generated functions |
| |
| pw::Status ChreApiTestService::ChreBleGetCapabilities( |
| const chre_rpc_Void &request, chre_rpc_Capabilities &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreBleGetCapabilities(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreBleGetFilterCapabilities( |
| const chre_rpc_Void &request, chre_rpc_Capabilities &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreBleGetFilterCapabilities(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreBleStartScanAsync( |
| const chre_rpc_ChreBleStartScanAsyncInput &request, |
| chre_rpc_Status &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreBleStartScanAsync(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreBleStopScanAsync( |
| const chre_rpc_Void &request, chre_rpc_Status &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreBleStopScanAsync(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreSensorFindDefault( |
| const chre_rpc_ChreSensorFindDefaultInput &request, |
| chre_rpc_ChreSensorFindDefaultOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreSensorFindDefault(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreGetSensorInfo( |
| const chre_rpc_ChreHandleInput &request, |
| chre_rpc_ChreGetSensorInfoOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreGetSensorInfo(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreGetSensorSamplingStatus( |
| const chre_rpc_ChreHandleInput &request, |
| chre_rpc_ChreGetSensorSamplingStatusOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreGetSensorSamplingStatus(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreSensorConfigure( |
| const chre_rpc_ChreSensorConfigureInput &request, |
| chre_rpc_Status &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreSensorConfigure(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreSensorConfigureModeOnly( |
| const chre_rpc_ChreSensorConfigureModeOnlyInput &request, |
| chre_rpc_Status &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreSensorConfigureModeOnly(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreAudioGetSource( |
| const chre_rpc_ChreHandleInput &request, |
| chre_rpc_ChreAudioGetSourceOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreAudioGetSource(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreConfigureHostEndpointNotifications( |
| const chre_rpc_ChreConfigureHostEndpointNotificationsInput &request, |
| chre_rpc_Status &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreConfigureHostEndpointNotifications(request, |
| response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::RetrieveLatestDisconnectedHostEndpointEvent( |
| const chre_rpc_Void &request, |
| chre_rpc_RetrieveLatestDisconnectedHostEndpointEventOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndRetrieveLatestDisconnectedHostEndpointEvent(request, |
| response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| pw::Status ChreApiTestService::ChreGetHostEndpointInfo( |
| const chre_rpc_ChreGetHostEndpointInfoInput &request, |
| chre_rpc_ChreGetHostEndpointInfoOutput &response) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| return validateInputAndCallChreGetHostEndpointInfo(request, response) |
| ? pw::OkStatus() |
| : pw::Status::InvalidArgument(); |
| } |
| |
| // End ChreApiTestService RPC generated functions |
| |
| // Start ChreApiTestService RPC sync functions |
| |
| void ChreApiTestService::ChreBleStartScanSync( |
| const chre_rpc_ChreBleStartScanAsyncInput &request, |
| ServerWriter<chre_rpc_GeneralSyncMessage> &writer) { |
| if (mWriter.has_value()) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| LOGE("ChreBleStartScanSync: a sync message already exists"); |
| return; |
| } |
| |
| mWriter = std::move(writer); |
| CHRE_ASSERT(mSyncTimerHandle == CHRE_TIMER_INVALID); |
| mRequestType = CHRE_BLE_REQUEST_TYPE_START_SCAN; |
| |
| chre_rpc_Status status; |
| if (!validateInputAndCallChreBleStartScanAsync(request, status) || |
| !status.status || !startSyncTimer()) { |
| sendFailureAndFinishCloseWriter(mWriter); |
| mSyncTimerHandle = CHRE_TIMER_INVALID; |
| LOGD("ChreBleStartScanSync: status: false (error)"); |
| } |
| } |
| |
| void ChreApiTestService::ChreBleStopScanSync( |
| const chre_rpc_Void &request, |
| ServerWriter<chre_rpc_GeneralSyncMessage> &writer) { |
| if (mWriter.has_value()) { |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| LOGE("ChreBleStopScanSync: a sync message already exists"); |
| return; |
| } |
| |
| mWriter = std::move(writer); |
| CHRE_ASSERT(mSyncTimerHandle == CHRE_TIMER_INVALID); |
| mRequestType = CHRE_BLE_REQUEST_TYPE_STOP_SCAN; |
| |
| chre_rpc_Status status; |
| if (!validateInputAndCallChreBleStopScanAsync(request, status) || |
| !status.status || !startSyncTimer()) { |
| sendFailureAndFinishCloseWriter(mWriter); |
| mSyncTimerHandle = CHRE_TIMER_INVALID; |
| LOGD("ChreBleStopScanSync: status: false (error)"); |
| } |
| } |
| |
| // End ChreApiTestService RPC sync functions |
| |
| // Start ChreApiTestService event functions |
| |
| void ChreApiTestService::GatherEvents( |
| const chre_rpc_GatherEventsInput &request, |
| ServerWriter<chre_rpc_GeneralEventsMessage> &writer) { |
| if (mEventWriter.has_value()) { |
| LOGE("GatherEvents: an event gathering call already exists"); |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| return; |
| } |
| |
| if (request.eventTypeCount > kMaxNumEventTypes) { |
| LOGE("GatherEvents: request.eventTypeCount is out of bounds"); |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| return; |
| } |
| |
| if (request.eventTypeCount == 0) { |
| LOGE("GatherEvents: request.eventTypeCount == 0"); |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| return; |
| } |
| |
| for (uint32_t i = 0; i < request.eventTypeCount; ++i) { |
| if (request.eventTypes[i] < std::numeric_limits<uint16_t>::min() || |
| request.eventTypes[i] > std::numeric_limits<uint16_t>::max()) { |
| LOGE("GatherEvents: invalid request.eventTypes: i: %" PRIu32, i); |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| writer.Finish(); |
| return; |
| } |
| |
| mEventTypes[i] = static_cast<uint16_t>(request.eventTypes[i]); |
| LOGD("GatherEvents: Watching for events with type: %" PRIu16, |
| mEventTypes[i]); |
| } |
| |
| mEventWriter = std::move(writer); |
| CHRE_ASSERT(mEventTimerHandle == CHRE_TIMER_INVALID); |
| mEventTimerHandle = chreTimerSet( |
| request.timeoutInNs, &mEventTimerHandle /* cookie */, true /* oneShot */); |
| if (mEventTimerHandle == CHRE_TIMER_INVALID) { |
| LOGE("GatherEvents: Cannot set the event timer"); |
| sendFailureAndFinishCloseWriter(mEventWriter); |
| mEventTimerHandle = CHRE_TIMER_INVALID; |
| } else { |
| mEventTypeCount = request.eventTypeCount; |
| mEventExpectedCount = request.eventCount; |
| mEventSentCount = 0; |
| LOGD("GatherEvents: mEventTypeCount: %" PRIu32 |
| " mEventExpectedCount: %" PRIu32, |
| mEventTypeCount, mEventExpectedCount); |
| } |
| } |
| |
| // End ChreApiTestService event functions |
| |
| void ChreApiTestService::handleBleAsyncResult(const chreAsyncResult *result) { |
| if (result == nullptr || !mWriter.has_value()) { |
| return; |
| } |
| |
| if (result->requestType == mRequestType) { |
| chreTimerCancel(mSyncTimerHandle); |
| mSyncTimerHandle = CHRE_TIMER_INVALID; |
| |
| chre_rpc_GeneralSyncMessage generalSyncMessage; |
| generalSyncMessage.status = result->success; |
| sendFinishAndCloseWriter(mWriter, generalSyncMessage); |
| LOGD("Active BLE sync function: status: %s", |
| generalSyncMessage.status ? "true" : "false"); |
| } |
| } |
| |
| void ChreApiTestService::handleGatheringEvent(uint16_t eventType, |
| const void *eventData) { |
| if (!mEventWriter.has_value()) { |
| return; |
| } |
| |
| bool matchedEvent = false; |
| for (uint32_t i = 0; i < mEventTypeCount; ++i) { |
| if (mEventTypes[i] == eventType) { |
| matchedEvent = true; |
| break; |
| } |
| } |
| if (!matchedEvent) { |
| LOGD("GatherEvents: Received event with type: %" PRIu16 |
| " that did not match any gathered events", |
| eventType); |
| return; |
| } |
| |
| LOGD("Gather events Received matching event with type: %" PRIu16, eventType); |
| |
| chre_rpc_GeneralEventsMessage message; |
| message.status = false; |
| switch (eventType) { |
| case CHRE_EVENT_SENSOR_ACCELEROMETER_DATA: { |
| message.status = true; |
| message.which_data = |
| chre_rpc_GeneralEventsMessage_chreSensorThreeAxisData_tag; |
| |
| const struct chreSensorThreeAxisData *data = |
| static_cast<const struct chreSensorThreeAxisData *>(eventData); |
| message.data.chreSensorThreeAxisData.header.baseTimestamp = |
| data->header.baseTimestamp; |
| message.data.chreSensorThreeAxisData.header.sensorHandle = |
| data->header.sensorHandle; |
| message.data.chreSensorThreeAxisData.header.readingCount = |
| data->header.readingCount; |
| message.data.chreSensorThreeAxisData.header.accuracy = |
| data->header.accuracy; |
| message.data.chreSensorThreeAxisData.header.reserved = |
| data->header.reserved; |
| |
| uint32_t numReadings = |
| MIN(data->header.readingCount, kThreeAxisDataReadingsMaxCount); |
| message.data.chreSensorThreeAxisData.readings_count = numReadings; |
| for (uint32_t i = 0; i < numReadings; ++i) { |
| message.data.chreSensorThreeAxisData.readings[i].timestampDelta = |
| data->readings[i].timestampDelta; |
| message.data.chreSensorThreeAxisData.readings[i].x = |
| data->readings[i].x; |
| message.data.chreSensorThreeAxisData.readings[i].y = |
| data->readings[i].y; |
| message.data.chreSensorThreeAxisData.readings[i].z = |
| data->readings[i].z; |
| } |
| break; |
| } |
| case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: { |
| const struct chreSensorSamplingStatusEvent *data = |
| static_cast<const struct chreSensorSamplingStatusEvent *>(eventData); |
| message.data.chreSensorSamplingStatusEvent.sensorHandle = |
| data->sensorHandle; |
| message.data.chreSensorSamplingStatusEvent.status.interval = |
| data->status.interval; |
| message.data.chreSensorSamplingStatusEvent.status.latency = |
| data->status.latency; |
| message.data.chreSensorSamplingStatusEvent.status.enabled = |
| data->status.enabled; |
| |
| message.status = true; |
| message.which_data = |
| chre_rpc_GeneralEventsMessage_chreSensorSamplingStatusEvent_tag; |
| break; |
| } |
| default: { |
| LOGE("GatherEvents: event type: %" PRIu16 " not implemented", eventType); |
| } |
| } |
| |
| if (!message.status) { |
| LOGE("GatherEvents: unable to create message for event with type: %" PRIu16, |
| eventType); |
| return; |
| } |
| |
| ChreApiTestManagerSingleton::get()->setPermissionForNextMessage( |
| CHRE_MESSAGE_PERMISSION_NONE); |
| pw::Status status = mEventWriter->Write(message); |
| CHRE_ASSERT(status.ok()); |
| ++mEventSentCount; |
| |
| if (mEventSentCount == mEventExpectedCount) { |
| chreTimerCancel(mEventTimerHandle); |
| mEventTimerHandle = CHRE_TIMER_INVALID; |
| finishAndCloseWriter(mEventWriter); |
| LOGD("GatherEvents: Finish"); |
| } |
| } |
| |
| void ChreApiTestService::handleTimerEvent(const void *cookie) { |
| if (mWriter.has_value() && cookie == &mSyncTimerHandle) { |
| sendFailureAndFinishCloseWriter(mWriter); |
| mSyncTimerHandle = CHRE_TIMER_INVALID; |
| LOGD("Active sync function: status: false (timeout)"); |
| } else if (mEventWriter.has_value() && cookie == &mEventTimerHandle) { |
| finishAndCloseWriter(mEventWriter); |
| mEventTimerHandle = CHRE_TIMER_INVALID; |
| LOGD("Timeout for event collection"); |
| } |
| } |
| |
| void ChreApiTestService::handleHostEndpointNotificationEvent( |
| const chreHostEndpointNotification *data) { |
| if (data->notificationType != HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT) { |
| LOGW("Received non disconnected event"); |
| return; |
| } |
| |
| ++mReceivedHostEndpointDisconnectedNum; |
| mLatestHostEndpointNotification = *data; |
| } |
| |
| void ChreApiTestService::copyString(char *destination, const char *source, |
| size_t maxChars) { |
| CHRE_ASSERT_NOT_NULL(destination); |
| CHRE_ASSERT_NOT_NULL(source); |
| |
| if (maxChars == 0) { |
| return; |
| } |
| |
| uint32_t i; |
| for (i = 0; i < maxChars - 1 && source[i] != '\0'; ++i) { |
| destination[i] = source[i]; |
| } |
| |
| memset(&destination[i], 0, maxChars - i); |
| } |
| |
| bool ChreApiTestService::startSyncTimer() { |
| mSyncTimerHandle = chreTimerSet( |
| kSyncFunctionTimeout, &mSyncTimerHandle /* cookie */, true /* oneShot */); |
| return mSyncTimerHandle != CHRE_TIMER_INVALID; |
| } |
| |
| // Start ChreApiTestManager functions |
| |
| bool ChreApiTestManager::start() { |
| chre::RpcServer::Service service = {.service = mChreApiTestService, |
| .id = 0x61002d392de8430a, |
| .version = 0x01000000}; |
| if (!mServer.registerServices(1, &service)) { |
| LOGE("Error while registering the service"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ChreApiTestManager::end() { |
| // do nothing |
| } |
| |
| void ChreApiTestManager::handleEvent(uint32_t senderInstanceId, |
| uint16_t eventType, |
| const void *eventData) { |
| if (!mServer.handleEvent(senderInstanceId, eventType, eventData)) { |
| LOGE("An RPC error occurred"); |
| } |
| |
| mChreApiTestService.handleGatheringEvent(eventType, eventData); |
| |
| switch (eventType) { |
| case CHRE_EVENT_BLE_ASYNC_RESULT: |
| mChreApiTestService.handleBleAsyncResult( |
| static_cast<const chreAsyncResult *>(eventData)); |
| break; |
| case CHRE_EVENT_TIMER: |
| mChreApiTestService.handleTimerEvent(eventData); |
| break; |
| case CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION: |
| mChreApiTestService.handleHostEndpointNotificationEvent( |
| static_cast<const chreHostEndpointNotification *>(eventData)); |
| break; |
| default: { |
| // ignore |
| } |
| } |
| } |
| |
| void ChreApiTestManager::setPermissionForNextMessage(uint32_t permission) { |
| mServer.setPermissionForNextMessage(permission); |
| } |
| |
| // End ChreApiTestManager functions |