| /* |
| * Copyright (C) 2021 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. |
| */ |
| |
| // TODO(b/298459533): remove_ap_wakeup_metric_report_limit ramp up -> remove old |
| // code |
| |
| #define LOG_TAG "ContextHubHal" |
| #define LOG_NDEBUG 1 |
| |
| #include "hal_chre_socket_connection.h" |
| |
| #include <log/log.h> |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| // TODO(b/298459533): Remove these when the flag_log_nanoapp_load_metrics flag |
| // is cleaned up |
| #include <aidl/android/frameworks/stats/IStats.h> |
| #include <android/binder_manager.h> |
| #include <android_chre_flags.h> |
| // TODO(b/298459533): Remove end |
| |
| #include <chre_atoms_log.h> |
| #include <utils/SystemClock.h> |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| |
| namespace android { |
| namespace hardware { |
| namespace contexthub { |
| namespace common { |
| namespace implementation { |
| |
| using ::android::chre::FragmentedLoadRequest; |
| using ::android::chre::FragmentedLoadTransaction; |
| using ::android::chre::HostProtocolHost; |
| using ::flatbuffers::FlatBufferBuilder; |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| // TODO(b/298459533): Remove these when the flag_log_nanoapp_load_metrics flag |
| // is cleaned up |
| using ::aidl::android::frameworks::stats::IStats; |
| using ::aidl::android::frameworks::stats::VendorAtom; |
| using ::aidl::android::frameworks::stats::VendorAtomValue; |
| using ::android::chre::Atoms::CHRE_AP_WAKE_UP_OCCURRED; |
| using ::android::chre::Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED; |
| using ::android::chre::flags::flag_log_nanoapp_load_metrics; |
| using ::android::chre::flags::remove_ap_wakeup_metric_report_limit; |
| // TODO(b/298459533): Remove end |
| |
| using ::android::chre::MetricsReporter; |
| using ::android::chre::Atoms::ChreHalNanoappLoadFailed; |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| |
| HalChreSocketConnection::HalChreSocketConnection( |
| IChreSocketCallback *callback) { |
| constexpr char kChreSocketName[] = "chre"; |
| |
| mSocketCallbacks = sp<SocketCallbacks>::make(*this, callback); |
| if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) { |
| ALOGE("Couldn't start socket client"); |
| } |
| } |
| |
| bool HalChreSocketConnection::getContextHubs( |
| ::chre::fbs::HubInfoResponseT *response) { |
| constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5); |
| ALOGV("%s", __func__); |
| |
| // If we're not connected yet, give it some time |
| // TODO refactor from polling into conditional wait |
| int maxSleepIterations = 250; |
| while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) { |
| std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
| } |
| |
| if (!mClient.isConnected()) { |
| ALOGE("Couldn't connect to hub daemon"); |
| } else if (!mHubInfoValid) { |
| // We haven't cached the hub details yet, so send a request and block |
| // waiting on a response |
| std::unique_lock<std::mutex> lock(mHubInfoMutex); |
| FlatBufferBuilder builder; |
| HostProtocolHost::encodeHubInfoRequest(builder); |
| |
| ALOGD("Sending hub info request"); |
| if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { |
| ALOGE("Couldn't send hub info request"); |
| } else { |
| mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout, |
| [this]() { return mHubInfoValid; }); |
| } |
| } |
| |
| if (mHubInfoValid) { |
| *response = mHubInfoResponse; |
| } else { |
| ALOGE("Unable to get hub info from CHRE"); |
| } |
| |
| return mHubInfoValid; |
| } |
| |
| bool HalChreSocketConnection::sendDebugConfiguration() { |
| FlatBufferBuilder builder; |
| HostProtocolHost::encodeDebugConfiguration(builder); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::sendMessageToHub(uint64_t nanoappId, |
| uint32_t messageType, |
| uint16_t hostEndpointId, |
| const unsigned char *payload, |
| size_t payloadLength) { |
| FlatBufferBuilder builder(1024); |
| HostProtocolHost::encodeNanoappMessage( |
| builder, nanoappId, messageType, hostEndpointId, payload, payloadLength); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::loadNanoapp( |
| FragmentedLoadTransaction &transaction) { |
| bool success = false; |
| std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex); |
| |
| if (mPendingLoadTransaction.has_value()) { |
| ALOGE("Pending load transaction exists. Overriding pending request"); |
| } |
| |
| mPendingLoadTransaction = transaction; |
| success = sendFragmentedLoadNanoAppRequest(mPendingLoadTransaction.value()); |
| if (!success) { |
| mPendingLoadTransaction.reset(); |
| } |
| |
| return success; |
| } |
| |
| bool HalChreSocketConnection::unloadNanoapp(uint64_t appId, |
| uint32_t transactionId) { |
| FlatBufferBuilder builder(64); |
| HostProtocolHost::encodeUnloadNanoappRequest( |
| builder, transactionId, appId, false /* allowSystemNanoappUnload */); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::queryNanoapps() { |
| FlatBufferBuilder builder(64); |
| HostProtocolHost::encodeNanoappListRequest(builder); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::requestDebugDump() { |
| FlatBufferBuilder builder; |
| HostProtocolHost::encodeDebugDumpRequest(builder); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::sendSettingChangedNotification( |
| ::chre::fbs::Setting fbsSetting, ::chre::fbs::SettingState fbsState) { |
| FlatBufferBuilder builder(64); |
| HostProtocolHost::encodeSettingChangeNotification(builder, fbsSetting, |
| fbsState); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::onHostEndpointConnected( |
| uint16_t hostEndpointId, uint8_t type, const std::string &package_name, |
| const std::string &attribution_tag) { |
| FlatBufferBuilder builder(64); |
| HostProtocolHost::encodeHostEndpointConnected(builder, hostEndpointId, type, |
| package_name, attribution_tag); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::onHostEndpointDisconnected( |
| uint16_t hostEndpointId) { |
| FlatBufferBuilder builder(64); |
| HostProtocolHost::encodeHostEndpointDisconnected(builder, hostEndpointId); |
| return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize()); |
| } |
| |
| bool HalChreSocketConnection::isLoadTransactionPending() { |
| std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex); |
| return mPendingLoadTransaction.has_value(); |
| } |
| |
| HalChreSocketConnection::SocketCallbacks::SocketCallbacks( |
| HalChreSocketConnection &parent, IChreSocketCallback *callback) |
| : mParent(parent), mCallback(callback) { |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| if (!remove_ap_wakeup_metric_report_limit()) { |
| mLastClearedTimestamp = elapsedRealtime(); |
| } |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::onMessageReceived( |
| const void *data, size_t length) { |
| if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) { |
| ALOGE("Failed to decode message"); |
| } |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::onConnected() { |
| ALOGI("Reconnected to CHRE daemon"); |
| if (mHaveConnected) { |
| ALOGI("Reconnected to CHRE daemon"); |
| mCallback->onContextHubRestarted(); |
| } |
| mParent.sendDebugConfiguration(); |
| mHaveConnected = true; |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::onDisconnected() { |
| ALOGW("Lost connection to CHRE daemon"); |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleNanoappMessage( |
| const ::chre::fbs::NanoappMessageT &message) { |
| ALOGD("Got message from nanoapp: ID 0x%" PRIx64, message.app_id); |
| mCallback->onNanoappMessage(message); |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| if (message.woke_host) { |
| // check and update the 24hour timer |
| std::lock_guard<std::mutex> lock(mNanoappWokeApCountMutex); |
| long nanoappId = message.app_id; |
| |
| if (!remove_ap_wakeup_metric_report_limit()) { |
| long timeElapsed = elapsedRealtime() - mLastClearedTimestamp; |
| if (timeElapsed > kOneDayinMillis) { |
| mNanoappWokeUpCount = 0; |
| mLastClearedTimestamp = elapsedRealtime(); |
| } |
| |
| mNanoappWokeUpCount++; |
| } |
| |
| if (remove_ap_wakeup_metric_report_limit() || |
| mNanoappWokeUpCount < kMaxDailyReportedApWakeUp) { |
| if (flag_log_nanoapp_load_metrics()) { |
| if (!mParent.mMetricsReporter.logApWakeupOccurred(nanoappId)) { |
| ALOGE("Could not log AP Wakeup metric"); |
| } |
| } else { |
| // create and report the vendor atom |
| std::vector<VendorAtomValue> values(1); |
| values[0].set<VendorAtomValue::longValue>(nanoappId); |
| |
| const VendorAtom atom{ |
| .atomId = CHRE_AP_WAKE_UP_OCCURRED, |
| .values{std::move(values)}, |
| }; |
| |
| mParent.reportMetric(atom); |
| } |
| } |
| } |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleHubInfoResponse( |
| const ::chre::fbs::HubInfoResponseT &response) { |
| ALOGD("Got hub info response"); |
| |
| std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex); |
| if (mParent.mHubInfoValid) { |
| ALOGI("Ignoring duplicate/unsolicited hub info response"); |
| } else { |
| mParent.mHubInfoResponse = response; |
| mParent.mHubInfoValid = true; |
| mParent.mHubInfoCond.notify_all(); |
| } |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleNanoappListResponse( |
| const ::chre::fbs::NanoappListResponseT &response) { |
| ALOGD("Got nanoapp list response with %zu apps", response.nanoapps.size()); |
| mCallback->onNanoappListResponse(response); |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleLoadNanoappResponse( |
| const ::chre::fbs::LoadNanoappResponseT &response) { |
| ALOGD("Got load nanoapp response for transaction %" PRIu32 |
| " fragment %" PRIu32 " with result %d", |
| response.transaction_id, response.fragment_id, response.success); |
| std::unique_lock<std::mutex> lock(mParent.mPendingLoadTransactionMutex); |
| |
| // TODO: Handle timeout in receiving load response |
| if (!mParent.mPendingLoadTransaction.has_value()) { |
| ALOGE( |
| "Dropping unexpected load response (no pending transaction " |
| "exists)"); |
| } else { |
| FragmentedLoadTransaction &transaction = |
| mParent.mPendingLoadTransaction.value(); |
| |
| if (!mParent.isExpectedLoadResponseLocked(response)) { |
| ALOGE("Dropping unexpected load response, expected transaction %" PRIu32 |
| " fragment %" PRIu32 ", received transaction %" PRIu32 |
| " fragment %" PRIu32, |
| transaction.getTransactionId(), mParent.mCurrentFragmentId, |
| response.transaction_id, response.fragment_id); |
| } else { |
| bool success = false; |
| bool continueLoadRequest = false; |
| if (response.success && !transaction.isComplete()) { |
| if (mParent.sendFragmentedLoadNanoAppRequest(transaction)) { |
| continueLoadRequest = true; |
| success = true; |
| } |
| } else { |
| success = response.success; |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| if (!success) { |
| if (flag_log_nanoapp_load_metrics()) { |
| if (!mParent.mMetricsReporter.logNanoappLoadFailed( |
| transaction.getNanoappId(), |
| ChreHalNanoappLoadFailed::TYPE_DYNAMIC, |
| ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC)) { |
| ALOGE("Could not log the nanoapp load failed metric"); |
| } |
| } |
| } |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| } |
| |
| if (!continueLoadRequest) { |
| mParent.mPendingLoadTransaction.reset(); |
| lock.unlock(); |
| mCallback->onTransactionResult(response.transaction_id, success); |
| } |
| } |
| } |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleUnloadNanoappResponse( |
| const ::chre::fbs::UnloadNanoappResponseT &response) { |
| ALOGV("Got unload nanoapp response for transaction %" PRIu32 |
| " with result %d", |
| response.transaction_id, response.success); |
| mCallback->onTransactionResult(response.transaction_id, response.success); |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleDebugDumpData( |
| const ::chre::fbs::DebugDumpDataT &data) { |
| ALOGV("Got debug dump data, size %zu", data.debug_str.size()); |
| mCallback->onDebugDumpData(data); |
| } |
| |
| void HalChreSocketConnection::SocketCallbacks::handleDebugDumpResponse( |
| const ::chre::fbs::DebugDumpResponseT &response) { |
| ALOGV("Got debug dump response, success %d, data count %" PRIu32, |
| response.success, response.data_count); |
| mCallback->onDebugDumpComplete(response); |
| } |
| |
| bool HalChreSocketConnection::isExpectedLoadResponseLocked( |
| const ::chre::fbs::LoadNanoappResponseT &response) { |
| return mPendingLoadTransaction.has_value() && |
| (mPendingLoadTransaction->getTransactionId() == |
| response.transaction_id) && |
| (response.fragment_id == 0 || |
| mCurrentFragmentId == response.fragment_id); |
| } |
| |
| bool HalChreSocketConnection::sendFragmentedLoadNanoAppRequest( |
| FragmentedLoadTransaction &transaction) { |
| bool success = false; |
| const FragmentedLoadRequest &request = transaction.getNextRequest(); |
| |
| FlatBufferBuilder builder(128 + request.binary.size()); |
| HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, request); |
| |
| if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { |
| ALOGE("Failed to send load request message (fragment ID = %zu)", |
| request.fragmentId); |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| if (flag_log_nanoapp_load_metrics()) { |
| if (!mMetricsReporter.logNanoappLoadFailed( |
| request.appId, ChreHalNanoappLoadFailed::TYPE_DYNAMIC, |
| ChreHalNanoappLoadFailed::REASON_CONNECTION_ERROR)) { |
| ALOGE("Could not log the nanoapp load failed metric"); |
| } |
| } else { |
| // create and report the vendor atom |
| std::vector<VendorAtomValue> values(3); |
| values[0].set<VendorAtomValue::longValue>(request.appId); |
| values[1].set<VendorAtomValue::intValue>( |
| ChreHalNanoappLoadFailed::TYPE_DYNAMIC); |
| values[2].set<VendorAtomValue::intValue>( |
| ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC); |
| |
| const VendorAtom atom{ |
| .atomId = CHRE_HAL_NANOAPP_LOAD_FAILED, |
| .values{std::move(values)}, |
| }; |
| reportMetric(atom); |
| } |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| |
| } else { |
| mCurrentFragmentId = request.fragmentId; |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED |
| // TODO(b/298459533): Remove this the flag_log_nanoapp_load_metrics flag is |
| // cleaned up |
| void HalChreSocketConnection::reportMetric(const VendorAtom atom) { |
| const std::string statsServiceName = |
| std::string(IStats::descriptor).append("/default"); |
| if (!AServiceManager_isDeclared(statsServiceName.c_str())) { |
| ALOGE("Stats service is not declared."); |
| return; |
| } |
| |
| std::shared_ptr<IStats> stats_client = IStats::fromBinder(ndk::SpAIBinder( |
| AServiceManager_waitForService(statsServiceName.c_str()))); |
| if (stats_client == nullptr) { |
| ALOGE("Failed to get IStats service"); |
| return; |
| } |
| |
| const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom); |
| if (!ret.isOk()) { |
| ALOGE("Failed to report vendor atom"); |
| } |
| } |
| // TODO(b/298459533): Remove end |
| #endif // CHRE_HAL_SOCKET_METRICS_ENABLED |
| |
| } // namespace implementation |
| } // namespace common |
| } // namespace contexthub |
| } // namespace hardware |
| } // namespace android |