/* * Copyright (C) 2023 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 "CapoDetector.h" #include #include #include #include #ifdef LOG_TAG #undef LOG_TAG #define LOG_TAG "CapoDetector" #endif namespace android { namespace chre { /** * Called when initializing connection with CHRE socket. */ sp CapoDetector::start() { sp listener = new CapoDetector(); if (!listener->connectInBackground(kChreSocketName, listener)) { ALOGE("Couldn't connect to CHRE socket"); return nullptr; } ALOGI("%s connect to CHRE socket.", __func__); return listener; } /** * Called when the socket is successfully (re-)connected. * Reset the position and try to send NanoappList request. */ void CapoDetector::onConnected() { flatbuffers::FlatBufferBuilder builder; // Reset the last position type. last_position_type_ = capo::PositionType::UNKNOWN; HostProtocolHost::encodeNanoappListRequest(builder); if (!sendMessage(builder.GetBufferPointer(), builder.GetSize())) { ALOGE("Failed to send NanoappList request"); // We don't return nullptr here so that we don't change the behavior } } /** * Called when we have failed to (re-)connect the socket after many attempts * and are giving up. */ void CapoDetector::onConnectionAborted() { ALOGE("%s, Capo Aborting Connection!", __func__); } /** * Invoked when the socket is disconnected, and this connection loss was not * the result of an explicit call to disconnect(). * Reset the position while disconnecting. */ void CapoDetector::onDisconnected() { last_position_type_ = capo::PositionType::UNKNOWN; } /** * Decode unix socket msgs to CHRE messages, and call the appropriate * callback depending on the CHRE message. */ void CapoDetector::onMessageReceived(const void *data, size_t length) { if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) { ALOGE("Failed to decode message"); } } /** * Listen for messages from capo nanoapp and handle the message. */ void CapoDetector::handleNanoappMessage(const fbs::NanoappMessageT &message) { ALOGI("%s, Id %" PRIu64 ", type %d, size %d", __func__, message.app_id, message.message_type, static_cast(message.message.size())); // Exclude the message with unmatched nanoapp id. if (message.app_id != kCapoNanoappId) return; // Handle the message with message_type. switch (message.message_type) { case capo::MessageType::ACK_NOTIFICATION: { capo::AckNotification gd; gd.set_notification_type(static_cast(message.message[1])); ALOGD("%s, get notification event from capo nanoapp, type %d", __func__, gd.notification_type()); break; } case capo::MessageType::POSITION_DETECTED: { uint8_t position; uint32_t time; { std::lock_guard lock(mCapoMutex); capo::PositionDetected gd; time = getCurrentTimeInMs(); gd.set_position_type(static_cast(message.message[1])); ALOGD("CapoDetector: [%u] get position event from capo nanoapp, from %d to %d", time, last_position_type_, gd.position_type()); // Record the last moment we were in FACE_UP state if (last_position_type_ == capo::PositionType::ON_TABLE_FACE_UP || gd.position_type() == capo::PositionType::ON_TABLE_FACE_UP) { mLastFaceUpEvent = time; } last_position_type_ = gd.position_type(); position = last_position_type_; } // Callback to function while getting carried position event. if (callback_func_ != nullptr) { ALOGD("%s, sent position type %d to callback function", __func__, last_position_type_); callback_func_(last_position_type_); } break; } default: ALOGE("%s, get invalid message, type: %" PRIu32 ", from capo nanoapp.", __func__, message.message_type); break; } } /** * Handle the response of a NanoappList request. * Ensure that capo nanoapp is running. */ void CapoDetector::handleNanoappListResponse(const fbs::NanoappListResponseT &response) { for (const std::unique_ptr &nanoapp : response.nanoapps) { if (nanoapp->app_id == kCapoNanoappId) { if (nanoapp->enabled) enable(); else ALOGE("Capo nanoapp not enabled"); return; } } ALOGE("Capo nanoapp not found"); } /** * Send enabling message to the nanoapp. */ void CapoDetector::enable() { // Create CHRE message with serialized message flatbuffers::FlatBufferBuilder builder, config_builder, force_builder; auto config_data = std::make_unique(); auto msg = std::make_unique(); config_data->set_still_time_threshold_nanosecond( mCapoDetectorMDParameters.still_time_threshold_ns); config_data->set_window_width_nanosecond(mCapoDetectorMDParameters.window_width_ns); config_data->set_motion_confidence_threshold( mCapoDetectorMDParameters.motion_confidence_threshold); config_data->set_still_confidence_threshold( mCapoDetectorMDParameters.still_confidence_threshold); config_data->set_var_threshold(mCapoDetectorMDParameters.var_threshold); config_data->set_var_threshold_delta(mCapoDetectorMDParameters.var_threshold_delta); msg->set_allocated_config_data(config_data.release()); auto pb_size = msg->ByteSizeLong(); auto pb_data = std::make_unique(pb_size); if (!msg->SerializeToArray(pb_data.get(), pb_size)) { ALOGE("Failed to serialize message."); } ALOGI("Configuring CapoDetector"); // Configure the detector from host-side ::android::chre::HostProtocolHost::encodeNanoappMessage( config_builder, getNanoppAppId(), capo::MessageType::CONFIGURE_DETECTOR, getHostEndPoint(), pb_data.get(), pb_size); ALOGI("Sending capo config message to Nanoapp, %" PRIu32 " bytes", config_builder.GetSize()); if (!sendMessage(config_builder.GetBufferPointer(), config_builder.GetSize())) { ALOGE("Failed to send config event for capo nanoapp"); } ALOGI("Enabling CapoDetector"); ::android::chre::HostProtocolHost::encodeNanoappMessage( builder, getNanoppAppId(), capo::MessageType::ENABLE_DETECTOR, getHostEndPoint(), /*messageData*/ nullptr, /*messageDataLenbuffer*/ 0); ALOGI("Sending enable message to Nanoapp, %" PRIu32 " bytes", builder.GetSize()); if (!sendMessage(builder.GetBufferPointer(), builder.GetSize())) { ALOGE("Failed to send enable event for capo nanoapp"); } ALOGI("Forcing CapoDetector to update state"); // Force an updated state upon connection ::android::chre::HostProtocolHost::encodeNanoappMessage( force_builder, getNanoppAppId(), capo::MessageType::FORCE_UPDATE, getHostEndPoint(), /*messageData*/ nullptr, /*messageDataLenbuffer*/ 0); ALOGI("Sending force-update message to Nanoapp, %" PRIu32 " bytes", force_builder.GetSize()); if (!sendMessage(force_builder.GetBufferPointer(), force_builder.GetSize())) { ALOGE("Failed to send force-update event for capo nanoapp"); } } /** * Method for gathering the position and time tuple simultaneously to avoid any * concurrency issues. */ void CapoDetector::getCarriedPositionInfo(uint8_t *position, uint32_t *time) { std::lock_guard lock(mCapoMutex); if (position) *position = last_position_type_; if (time) *time = mLastFaceUpEvent; } } // namespace chre } // namespace android